<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
  <link href="//gmpg.org/xfn/11" rel="profile">
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="Hugo 0.68.3" />

  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>我的博客</title>

  
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/print.css" media="print">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/poole.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/syntax.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/hyde.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abril+Fatface|PT+Sans:400,400i,700">


  
  <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
  <link rel="shortcut icon" href="/favicon.png">

  
  <link href="/my_technology_blog/index.xml" rel="alternate" type="application/rss+xml" title="我的博客" />
  
</head>

  <body class="theme-base-0b ">
  <aside class="sidebar">
  <div class="container sidebar-sticky">
    <div class="sidebar-about">
      <a href="/my_technology_blog/"><h1>我的博客</h1></a>
      <p class="lead">
       杨博的博客 
      </p>
    </div>

    <nav>
      <ul class="sidebar-nav">
        <li><a href="/my_technology_blog/">Home</a> </li>
        
      </ul>
    </nav>

    <p>&copy; 2021. All rights reserved. </p>
  </div>
</aside>

    <main class="content container">
    <div class="posts">
<article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E7%BC%96%E5%86%99%E6%A6%82%E8%A6%81/">编写测试计划和设计用例</a>
  </h1>
  <time datetime="2021-04-29T20:26:37&#43;0800" class="post-date">Thu, Apr 29, 2021</time>
  1、设计用例 设计用例时为了某个业务目标编写的一组由测试输入，执行条件以及预期结果的案例
编写测试用例的几个必要条件
用例编号(一般用项目名缩写下划线模块名缩写下划线编号)，所属模块，用例标题，优先级(一般正向测试为高，反向为中)，前置条件，输入数据，操作步骤，预期结果，实际结果，是否通过，测试人员，测试时间
测试用例示例
 ##2、编写测试用例的基本方法
2.1等价类划分法 多用在输入框概念：分步骤把海量的数据的测试用例集减的很小，但过程同样有效；
​	等价类：某个输入域的集合，在这个输入域中每个输入条件都是等效的。一般可分为有效等价类和无效等价类​	有效等价类：指符合《需求规格说明书》，输入合理的数据集合
​	无效等价类：指不符合《需求规格说明书》，输入不合理的数据集合
示例
示例
比如：一个青少年考试的分数（备注13-17岁为青少年） 假设青少年年龄为x,13&lt;=x&lt;=17,数学成绩为y：0&lt;=y&lt;=100 那么年龄按照等价类划分可分为x&lt;13,13&lt;=x&lt;=17,x&gt;17,有效等价类是13&lt;=x&lt;=17，无效等价类是x&lt;13，x&gt;17 数学成绩按照等价类划分可分为y&lt;0,0&lt;=y&lt;=100,y&gt;100,有效等价类是0&lt;=y&lt;=100，无效等价类是y&lt;0，y&gt;100 ###2.2边界值法
多用在有&lt;=或&gt;=概念：一般边界值分析是因为程序开发循环体时的取数可能会因为&lt;,&lt;=搞错。
使用边界值法选取关键的原则是：边界条件，次边界条件，空值，无效数据
确定边界值方法
选取正好等于、刚刚大于和刚好小于边界值的数据作为测试数据边界值不是从每个等价类中挑一个作为代表，而是把每个等价类的边界都进行测试。示例
输入要求是1 ～ 100之间的整数，因此自然产生了1和100两个边界，我们在设计测试用例的时，要重点考虑这两个边界问题。
###2.3因果图法
概念：因果图法比较适合输条件比较多的情况，测试所有的输入条件的排列组合。所谓的原因就是输入，所谓的结果就是输出。
因果图基本图形符号：
恒等：若原因出现，则结果出现；若原因不出现，则结果不出现。
非（～）：若原因出现，则结果不出现；若原因不出现，则结果出现。
或（∨）：若几个原因中有一个出现，则结果出现；若几个原因都不出现，则结果不出现。
与（∧）：若几个原因都出现，结果才出现；若其中有一个原因不出现，则结果不出现。
因果图的约束符号：
E（互斥）：表示两个原因不会同时成立，两个中最多有一个可能成立
I（包含）：表示三个原因中至少有一个必须成立
O（惟一）：表示两个原因中必须有一个，且仅有一个成立
R（要求）：表示两个原因，a出现时，b也必须出现，a出现时，b不可能不出现
M（屏蔽）：两个结果，a为1时，b必须是0，当a为0时，b值不定
示例：
例如：有一个处理单价为2.5元的盒装饮料的自动售货机软件。若投入2.5元硬币，按“可乐”、“啤酒”、或“奶茶”按钮，相应的饮料就送出来。若投入的是3元硬币，在送出饮料的同时退还5角硬币。
分析这一段说明，我们可列出原因和结果
原因(输入)：
投入2.5元硬币；
投入3元；
按“可乐”按钮；
按“啤酒”按钮；
按“奶茶”按钮。
中间状态： ① 已投币；②已按钮
结果(输出)：
退还5角硬币；
送出“可乐”饮料；
送出“啤酒”饮料；
送出“奶茶”饮料；
判定表法 ###2.4.场景法
测试用例设计的思想
现在的软件几乎都是用事件触发来控制流程的，事件触发时的情景便形成了场景，而同一事件不同的触发顺序和处理结果就形成事件流。这种在软件设计方面的思想也可以引入到软件测试中，可以比较生动地描绘出事件触发时的情景，有利于测试设计者设计测试用例，同时使测试用例更容易理解和执行。
用例场景是通过描述流经用例的路径来确定的过程，
这个流经过程要从用例开始到结束遍历其中所有基本流和备选流。
遵循上图中每个经过用例的可能路径，可以确定不同的用例场景。从基本流开始，再将基本流和备选流结合起来，可以确定以下用例场景：
基本流和备选流的区别
银行案例ATM:
个人标识号 (PIN＝personal identification number )，用于保护智能卡免受误用的秘密标识代码。PIN 与密码类似，只有卡的所有者才知道该 PIN。只有拥有该智能卡并知道 PIN 的人才能使用该智能卡
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E7%BC%96%E5%86%99%E6%A6%82%E8%A6%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95%E5%9F%BA%E7%A1%80/">软件测试基础</a>
  </h1>
  <time datetime="2021-04-29T19:23:21&#43;0800" class="post-date">Thu, Apr 29, 2021</time>
  ###1、软件架构分类
B\S架构
​	浏览器-服务器。例如百度这种网页。
C\S架构
​	客户端-服务器。例如微信这种需要下载程序的。
区别：
 因为B\S架构只用开发服务端，而C\S是客户端和服务端都需要开发，所以B\S架构开发效率比C\S架构开发效率高。 B\S架构只有服务端在处理数据，而C\S架构客户端也会进行一些数据处理，所以C\S架构执行效率更高。 B\S架构用的http协议进行传输，但是http协议是明文传输的，容易造成信息泄露，所以B\S架构相对来说不如C\S安全 B\S升级只需要升级服务器，用户在浏览器上只需要刷新就可以了；而C\S架构服务器和客户端都需要升级。  2、测试模型 V模型
V模型的生命周期：
V模型只把测试当作在需求分析、系统设计以及编码后的一个阶段，但是却忽略了测试对需求分析、系统设计的验证，需求的满足到最后才会做验证。
解决思路：当软件在开发时，研发人员与测试人员同步进行工作，这样可以尽早发现系统中的BUG，不会到最后测试时才发现BUG，导致解决了BUG，又出现了新的BUG。
V模型的优点：
 每个阶段都清晰明了，便于控制开发的每个过程 既包含单元测试，又包含系统测试  V模型的缺点：
 测试介入较晚，对于前期的一些缺陷无从发现和修改 测试和开发串行  W模型
W模型是V模型的发展，相对于V模型，W模型测试人员介入更早，伴随着软件开发的整个生命周期，而且W模型测试的不仅仅是程序，也会对需求、功能、设计进行测试。
W模型的生命周期：
W模型的优点：
 测试介入早，能够尽早地发现系统中的缺陷 测试伴随整个软件开发的生命周期，不是仅仅测试程序，而且会测试需求、设计，软件的开发过程更加科学，从而有利于尽量减少BUG 测试和开发是独立进行的  W模型的缺点：
 在一些开发过程中，没有文档的产生，这种情况不适合使用W模型 W模型对于测试人员和开发人员的要求更高。  3、软件测试的流程 测试准备阶段：项目立项、需求分析、需求评审。产生需求文档和产品需求说明书
测试计划阶段：编写测试计划，进行测试计划评估。产生测试计划
测试设计阶段：根据测试计划提取测试点和编写测试用例，测试用例评估。 产生测试用例
测试执行阶段：对核心功能进行冒烟测试，执行测试用例，提交BUG，回归测试。产生缺陷报告
测试完成阶段：进行验收测试，编写测试报告、项目上线。产生测试报告
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/django-%E4%BD%BF%E7%94%A8%E7%8E%B0%E6%9C%89%E6%95%B0%E6%8D%AE%E5%BA%93%E7%94%9F%E6%88%90-models/">Django 使用现有数据库生成 models</a>
  </h1>
  <time datetime="2020-09-07T18:47:21&#43;0800" class="post-date">Mon, Sep 7, 2020</time>
  通常，我们是先建立 Django 服务，然后通过 Django 的 models 生成数据库。然而，有些时候我们需要反向操作，即从现有数据库生成 Django 的 models。比如，我们需要针对当前数据库，使用 Django 制作 REST 的接口。
1 Django 配置数据库连接参数 新建 Djano项目、app（假设名叫 blogapp）。在 settings.py 中设置数据库的连接参数，参照 Django 与 MySQL 数据库的连接。
2 根据数据库生成 models 首先，需要用 inspectdb 来生成 models.py，主要的命令是
python manage.py inspectdb 假设指定 models，比如 blogapp 下面的 models
python manage.py inspectdb &gt; blogapp/models.py 3 models 中的调整 managed = True 对于自己需要的 models，如果需要以后进行 CRUD 增删查改操作，需要在 Meta 中设置 managed = True。
class PostsPost(models.Model): title = models.CharField(max_length=150) content = models.TextField() timestamp = models.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/django-%E4%BD%BF%E7%94%A8%E7%8E%B0%E6%9C%89%E6%95%B0%E6%8D%AE%E5%BA%93%E7%94%9F%E6%88%90-models/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/mysql%E4%B8%ADin%E5%92%8Cexist/">mysql中in和exist</a>
  </h1>
  <time datetime="2020-08-07T21:02:56&#43;0800" class="post-date">Fri, Aug 7, 2020</time>
  exists和in in 是把外表和内表作hash join，而exists是对外表作loop，每次loop再对内表进行查询。 如：
A：select * from t1 a where exists (select * from t2 b where b.id = a.id) B：select * from t1 a where a.id in (select b.id from t2 b) 对于A，用到了t2上的id索引，exists执行次数为t1.length，不缓存exists的结果集。 对于B，用到了t1上的id索引，首先执行in语句，然后将结果缓存起来，之后遍历t1表，将满足结果的加入结果集，所以执行次数为t1.length*t2.length次。 因此对t1表大t2表小的情况使用in，t2表小t1表大的情况使用exists
not exists和not in A：select * from t1 a where not exists (select * from t2 b where b.id = a.id) B：select * from t1 a where a.id not in (select b.id from t2 b) 对于A，和exists一样，用到了t2上的id索引，exists()执行次数为t1.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/mysql%E4%B8%ADin%E5%92%8Cexist/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/mysql%E9%92%9Finnodb%E5%92%8Cmyisam%E7%9A%84%E5%8C%BA%E5%88%AB/">mysql钟innodb和myisam的区别</a>
  </h1>
  <time datetime="2020-06-29T15:50:54&#43;0800" class="post-date">Mon, Jun 29, 2020</time>
  区别：   InnoDB 支持事务，MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一；
  InnoDB 支持外键，而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败；
  InnoDB 是聚集索引，MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上，因此 InnoDB 必须要有主键，通过主键索引效率很高。但是辅助索引需要两次查询，先查询到主键，然后再通过主键查询到数据。因此，主键不应该过大，因为主键太大，其他索引也都会很大。而 MyISAM 是非聚集索引，数据文件是分离的，索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  InnoDB 不保存表的具体行数，执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数，执行上述语句时只需要读出该变量即可，速度很快；
  InnoDB 最小的锁粒度是行锁，MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表，导致其他查询和更新都会被阻塞，因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一；
  如何选择：   是否要支持事务，如果要请选择 InnoDB，如果不需要可以考虑 MyISAM；
  如果表中绝大多数都只是读查询，可以考虑 MyISAM，如果既有读写也挺频繁，请使用InnoDB。
  系统奔溃后，MyISAM恢复起来更困难，能否接受，不能接受就选 InnoDB；
  MySQL5.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/mysql%E9%92%9Finnodb%E5%92%8Cmyisam%E7%9A%84%E5%8C%BA%E5%88%AB/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E4%B8%AD%E7%9A%84%E9%94%81/">python中的锁</a>
  </h1>
  <time datetime="2020-06-29T10:48:53&#43;0800" class="post-date">Mon, Jun 29, 2020</time>
  一、全局解释器锁（GIL），二、同步锁， 三、递归锁和死锁， 四、信号量（semaphore）
一、全局解释器锁（GIL） 全局解释器锁能够保证同一时刻只有一个线程在运行，避免了线程之间相互争夺资源Python 虚拟机默认使用的是 CPython 解释器(C 语言实现)，CPython 使用了 GIL (Golbal Iterpreter Lock - 全局解释器锁)，来确保同一时间只有一个线程运行，所以即使再多的线程也只能有效的使用一个 CPU。
为什么不删除 GIL:
实验证明，如果放弃 GIL，使用大量细粒度的锁代替，导致单线程性能下降至少 30%。所以说 GIL 在支持多线程的同时能把单线程的优势最大地发挥出来。
import time import threading def sub(): global num num -= 1 time.sleep(1) num = 100 # 定义一个全局变量 l = [] # 定义一个空列表，用来存放所有的列表 for i in range(100): # for循环100次 t = threading.Thread(target=sub) #每次循环开启一个线程 t.start() # 开启线程 l.append(t) # 将线程加入列表l for i in l: i.join() # 这里加上join保证所有的线程结束后才运行下面的代码 print(num) # 输出结果为0 二、线程锁 　当一个线程对某个资源进行CPU计算的操作时加一个线程锁，只有当前线程计算完成主动释放锁，其他线程才能对其操作
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E4%B8%AD%E7%9A%84%E9%94%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/uwsgiwsgi%E5%92%8Cnginx%E7%9A%84%E5%8C%BA%E5%88%AB%E5%92%8C%E5%85%B3%E7%B3%BB/">uwsgi、wsgi和nginx的区别和关系</a>
  </h1>
  <time datetime="2020-06-28T15:13:21&#43;0800" class="post-date">Sun, Jun 28, 2020</time>
  区分uWSGI和WSGI 在python web开发中，我们经常使用uwsgi配合nginx部署一个web框架，如Django或flask。同时我们又会说，框架和web服务器之间要符合WSGI协议。那就来厘清一下这几个概念。
web服务器和web框架 在讲uWSGI和WSGI之前，先要弄清楚web开发的两大块，web服务器和web框架。 web服务器即用来接受客户端请求，建立连接，转发响应的程序。至于转发的内容是什么，交由web框架来处理，即处理这些业务逻辑。如查询数据库、生成实时信息等。Nginx就是一个web服务器，Django或flask就是web框架。
回到uWSGI和WSGI。 那么如何实现uWSGI和WSGI的配合呢？如何做到任意一个web服务器，都能搭配任意一个框架呢？这就产生了WSGI协议。只要web服务器和web框架满足WSGI协议，它们就能相互搭配。所以WSGI只是一个协议，一个约定。而不是python的模块、框架等具体的功能。
而uWSGI，则是实现了WSGI协议的一个web服务器。即用来接受客户端请求，转发响应的程序。实际上，一个uWSGI的web服务器，再加上Django这样的web框架，就已经可以实现网站的功能了。那为什么还需要Nginx呢？
为什么需要Nginx 一个普通的个人网站，访问量不大的话，当然可以由uWSGI和Django构成。但是一旦访问量过大，客户端请求连接就要进行长时间的等待。这个时候就出来了分布式服务器，我们可以多来几台web服务器，都能处理请求。但是谁来分配客户端的请求连接和web服务器呢？Nginx就是这样一个管家的存在，由它来分配。这也就是由Nginx实现反向代理，即代理服务器。
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/mysql%E5%9F%BA%E7%A1%80%E7%9B%B8%E5%85%B3/">mysql基础相关</a>
  </h1>
  <time datetime="2020-06-26T14:34:16&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  事物隔离级别 1 未提交读: 脏读（READ UNCOMMITTED）
​	1）事务2查询到的数据是事务1中修改但未提交的数据，但因为事务1回滚了数据
​	2）所以事务2查询的数据是不正确的，因此出现了脏读的问题。
2 提交读: 不可重复读（READ COMMITTED）
​	1）事务2执行update语句但未提交前，事务1的前两个select操作返回结果是相同的。
​	2）但事务2执行commit操作后，事务1的第三个select操作就读取到事务2对数据的改变。
​	3）导致与前两次select操作返回不同的数据，因此出现了不可重复读的问题。
3 可重复读: 幻读（REPEATABLE READ）：这是MySQL的默认事务隔离级别
​	1）事务每开启一个实例，都会分配一个版本号给它，如果读取的数据行正在被其它事务执行DELETE或UPDATE操作（即该行上有排他锁）
​	2）这时该事物的读取操作不会等待行上的锁释放，而是根据版本号去读取行的快照数据（记录在undo log中）
​	3）这样，事务中的查询操作返回的都是同一版本下的数据，解决了不可重复读问题。
​	4）虽然该隔离级别下解决了不可重复读问题，但理论上会导致另一个问题：幻读（Phantom Read）。
​	5）一个事务在执行过程中，另一个事物对已有数据行的更改，MVCC机制可保障该事物读取到的原有数据行的内容相同
​	6）但并不能阻止另一个事务插入新的数据行，这就会导致该事物中凭空多出数据行，像出现了幻读一样，这便是幻读问题。
4 可串行读（SERIALIZABLE）
​	1）这是事务的最高隔离级别，通过强制事务排序，使之不可能相互冲突，就是在每个读的数据行加上共享锁来实现
​	2）在该隔离级别下，可以解决前面出现的脏读、不可重复读和幻读问题，但也会导致大量的超时和锁竞争现象，一般不推荐使用
锁 1. 乐观锁
定义：
​	总是假设最好的情况，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用类型，这样可以提高吞吐量
  每次获取商品时，不对该商品加锁。
  在更新数据的时候需要比较程序中的库存量与数据库中的库存量是否相等，如果相等则进行更新
  反之程序重新获取库存量，再次进行比较，直到两个库存量的数值相等才进行数据更新。
  #### 乐观锁实现加一操作代码 # 我们可以看到，只有当对数量-1操作时才会加锁，只有当程序中值和数据库中的值相等时才正真执行。 &#39;&#39;&#39; //不加锁 select id,name,stock where id=1; //业务处理 begin; update shop set stock=stock-1 where id=1 and stock=stock; commit; &#39;&#39;&#39; 2.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/mysql%E5%9F%BA%E7%A1%80%E7%9B%B8%E5%85%B3/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E5%9B%9B%E5%A4%A7%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0/">python四大高阶函数（map、reduce、filter、sorted）</a>
  </h1>
  <time datetime="2020-06-26T14:20:45&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  map()  map() 函数接受两个参数，一个是函数，一个是序列化，map将传入的函数一次作用到序列的每一个元素，并把结果作为新的list返回 会根据提供的函数对指定的序列做映射   返回值 python2：是列表 python3：迭代器  1、map()用于字典
a = {&#39;a&#39;: 1, &#39;b&#39;: 2} la = map(str, a) print(list(la)) # [&#39;a&#39;, &#39;b&#39;] 2、map()用于元组
b = (1,2,3,4,5,6) li = map(str,b) print(list(li)) # [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;, &#39;5&#39;, &#39;6&#39;] 2、map()用于字符串
c = &#34;laowang&#34; lc = map(str,c) print(list(lc)) # [&#39;l&#39;, &#39;a&#39;, &#39;o&#39;, &#39;w&#39;, &#39;a&#39;, &#39;n&#39;, &#39;g&#39;] reduce()  定义 reduce() 函数会对参数序列中的元素进行累积  格式
reduce(function, iterable[,initializer]) function:函数---》有两个参数 iterable：---》可迭代对象 initializer：可以选，初始参数  from functools import reduce def add(a, b): return a + b add(1, 2) aa = reduce(lambda x,y:x + y, [1,2,3,4,5,6]) print(aa) # 21 获取每一个单词出现的次数
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E5%9B%9B%E5%A4%A7%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E4%B8%89%E5%99%A8/">python三器</a>
  </h1>
  <time datetime="2020-06-26T13:55:39&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  装饰器 1. 装饰器定义
​	在不修改源代码的基础上增加新的功能
2. 使用高阶函数模拟装饰器
#! /usr/bin/env python # -*- coding: utf-8 -*- import time def timer(func): start_time = time.time() func() print &#39;函数执行时间为&#39;, time.time() - start_time def test(): print &#39;开始执行test&#39; time.sleep(3) print &#39;test执行结束&#39; timer(test) &#39;&#39;&#39; 开始执行test test执行结束 函数执行时间为 3.00332999229 &#39;&#39;&#39; 3. 计算运行时间装饰器
import time def timer(func): #timer(test1) func=test1 def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) #run test1 stop_time = time.time() print(&#34;running time is %s&#34;%(stop_time-start_time)) return deco @timer # test1=timer(test1) def test1(): time.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E4%B8%89%E5%99%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E5%B0%81%E8%A3%85%E7%BB%A7%E6%89%BF%E5%A4%9A%E6%80%81/">封装、继承、多态</a>
  </h1>
  <time datetime="2020-06-26T13:52:14&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  1. 封装
​	1．在类中对数据的赋值、内部调用对外部用户是透明的
 这使类变成了一个胶囊或容器，里面包含着类的数据和方法
  作用：
  　1）防止数据被随意修改
　2）使外部程序不需要关注对象内部的构造，只需要通过对外提供的接口进行直接访问
2 ．继承（代码重用）
  一个类可以派生出子类，在这个父类里定义的属性、方法自动被子类继承
  比如CS中的警察和恐怖分子，可以将两个角色的相同点写到一个父类中，然后同时去继承它
  使用经典类： Person.init(self,name,age) 并重写写父类Person的构造方法，实现，先覆盖，再继承，再重构
  3.多态（接口重用）
​	1．多态是面向对象的重要特性,简单点说:“一个接口，多种实现”
 指一个基类中派生出了不同的子类，且每个子类在继承同样的方法名的同时又对父类的方法做了不同的实现
  这就是同一种事物表现出的多种形态
  比如黄种人继承了人talk这个功能，但是他说的是中文，而美国人的talk是英文，但是他们是同样的talk
**作用：**简单的讲就是允许父类调用子类的方法
  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E4%B8%89%E7%A8%8B/">python三程</a>
  </h1>
  <time datetime="2020-06-26T13:05:56&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  进程 1. 进程定义
  进程是资源分配最小单位
  当一个可执行程序被系统执行（分配内存等资源）就变成了一个进程
  进程定义拓展回答内容
  程序并不能单独运行，只有将程序装载到内存中，系统为它分配资源才能运行，这种执行的程序就称之为进程
  程序和进程的区别就在于：程序是指令的集合，它是进程运行的静态描述文本；进程是程序的一次执行活动，属于动态概念
  在多道编程中，我们允许多个程序同时加载到内存中，在操作系统的调度下，可以实现并发地执行。
  进程的出现让每个用户感觉到自己独享CPU，因此，进程就是为了在CPU上实现多道编程而提出的。
  进程之间有自己独立的内存，各进程之间不能相互访问
  创建一个新线程很简单，创建新进程需要对父进程进行复制
  多道编程概念
多道编程： 在计算机内存中同时存放几道相互独立的程序，他们共享系统资源，相互穿插运行
单道编程： 计算机内存中只允许一个的程序运行
2. 有了进程为什么还要线程？
  进程优点：
提供了多道编程，让我们感觉我们每个人都拥有自己的CPU和其他资源，可以提高计算机的利用率
  进程的两个重要缺点
a. 第一点：进程只能在一个时间干一件事，如果想同时干两件事或多件事，进程就无能为力了。
b. 第二点：进程在执行的过程中如果阻塞，即使进程中有些工作不依赖于输入的数据，也将无法执行（例如等待输入，整个进程就会挂起）。
c. 例如，我们在使用qq聊天， qq做为一个独立进程如果同一时间只能干一件事，那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息
d. 你会说，操作系统不是有分时么？分时是指在不同进程间的分时呀
e. 即操作系统处理一会你的qq任务，又切换到word文档任务上了，每个cpu时间片分给你的qq程序时，你的qq还是只能同时干一件事呀
  3. 进程间互相访问数据的四种方法
**注：**不同进程间内存是不共享的，所以互相之间不能访问对方数据
法1: 利用Queues实现父进程到子进程（或子进程间）的数据传递
法2: 使用管道pipe实现两个进程间数据传递
法3: Managers实现很多进程间数据共享
法4：借助redis中间件进行数据共享
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E4%B8%89%E7%A8%8B/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%88%97%E8%A1%A8%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E5%85%B8%E9%9B%86%E5%90%88/">python基础之列表、字符串、字典、集合</a>
  </h1>
  <time datetime="2020-06-26T12:08:28&#43;0800" class="post-date">Fri, Jun 26, 2020</time>
  列表 1. append用于在列表末尾追加新的对象
* a = [1,2,3] a.append(4) #the result： [1, 2, 3, 4] 2、count方法统计某个元素在列表中出现的次数
a = [&#39;aa&#39;,&#39;bb&#39;,&#39;cc&#39;,&#39;aa&#39;,&#39;aa&#39;] print(a.count(&#39;aa&#39;)) #the result ： 3 3. extend方法可以在列表的末尾一次性追加另一个序列中的多个值
a = [1,2,3] b = [4,5,6] a.extend(b) #the result ：[1, 2, 3, 4, 5, 6] 4. index函数用于从列表中找出某个值第一个匹配项的索引位置
a = [1,2,3,1] print(a.index(1)) #the result ： 0 5. insert方法用于将对象插入到列表中
a = [1,2,3] a.insert(0,&#39;aa&#39;) #the result : [&#39;aa&#39;, 1, 2, 3] 6. pop方法会移除列表中的一个元素（默认是最后一个），并且返回该元素的值**
a = [1,2,3] a.pop() a.pop(0) #the result ： [1, 2] 7.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%88%97%E8%A1%A8%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E5%85%B8%E9%9B%86%E5%90%88/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/mysql%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/">Mysql增删改查</a>
  </h1>
  <time datetime="2020-06-23T17:08:31&#43;0800" class="post-date">Tue, Jun 23, 2020</time>
  增：
1、save:
用法：给数据库表名赋值：例：user = model.User()
复制的变量就等于原表，给变量的想要添加的字段中添加值，
​ 例： user.name = &lsquo;杨博&rsquo;
save添加进表中： user.save()
有外键的表添加数据：先查询出要添加的地方，因为要添加数据不是直接对应表，而是主键所在表中字段的数据，所以要往有外键的表中添加数据就要先查询出主键所对应的数据相对应的表 s = models.School.objects.filter(&lsquo;积云教育&rsquo;).first()
实例化表： class = models.Class() class.name = &#39;1811&#39; class.school = &#39;s&#39; # 外键所对应的主表 class.save() 要添加多条数据一条一条添加，注意所用的变量名不能相同 ​
2、直接添加：
例：
model.User.objects.create(name=&#39;杨博&#39;) 表名 字段名 删：
delete
先查出来再delete直接删除
1、models.User.objects.filter(name=&#39;杨博&#39;).first().delete() 2、models.User.objects.get(name=&#39;杨博&#39;).delete() 改：
1、先查出来要修改的内容，再用增加的方法增添
user = models.User.object.filter(name = &#39;杨博&#39;).first() user.name = &#39;张三&#39; user.save() 2、update 查出来的必须是结果集（列表），也就是只能用filter方法查询，并且不能跟first().
Models.User.objects.filter(name=&#39;杨博&#39;).update(name=&#39;张三&#39;) html中修改数据，后台·post需要获取id用隐藏获取
hidden 该行内容隐藏，用来传输id到post
defpost(self,request): s_id=request.POST.get(&#39;id&#39;) name=request.POST.get(&#39;name1&#39;) grade=request.POST.get(&#39;grade&#39;) ifall([s_id,name,grade]): s=models.Student.objects.filter(id=s_id).first() s.name=name s.grade=grade s.save() 查：
1、get查询
  
  <div class="read-more-link">
    <a href="/my_technology_blog/mysql%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%802/">Python基础2</a>
  </h1>
  <time datetime="2020-06-23T17:04:02&#43;0800" class="post-date">Tue, Jun 23, 2020</time>
  字符串格式化 Python的字符串格式化有两种方式
 %格式符方式 format方式  事实上，格式化借助的是对象的魔术方法。 实现了__format__的对象才适用于format方式格式化输出， 实现了__str__，__repr__的对象才分别适用于格式符%s和%r输出。 在面向对象章节有给对象定义魔术方法的示例。
%格式符 一般格式为 %(name) + format_spec
[[fill]align][sign][#][0][width][grouping_option][.precision][type] name 可选，用于选择指定的key
&gt;&gt;&gt; &#34;%(name)s——%(age)d&#34;%{&#39;name&#39;:&#39;宝钟玛琳&#39;,&#39;age&#39;:17}&#39;宝钟玛琳——17&#39; s 和 d 属于 [type]
format_spec规则的常用组分：
sign 可选，数字和字符串可供选择的值有：
 + 右对齐；正数负数前加正负号 - 左对齐；负数前加负号 空格 右对齐；负数前加负号 0 右对齐；正数前无符号，负数前加负号；用0填充空白处  width 可选，占有宽度
&gt;&gt;&gt; &#34;%(name)+7s——%(age)-5d&#34;%{&#39;name&#39;:&#39;宝钟玛琳&#39;,&#39;age&#39;:17}&#39; 宝钟玛琳——17 &#39; .precision 可选，小数点后保留的位数 默认6位
type 类型，必选
任意对象：
 %s 字符串 (采用str()的显示) %r 字符串 (采用repr()的显示) %c 单个字符  字符串和整数：
 %b 二进制整数 %% 字符”%”转义  数字：
 %d 十进制整数 %i 十进制整数 %o 八进制整数 %x 十六进制整数 %e 指数 (基底写为e) %E 指数 (基底写为E) %f 浮点数 %F 浮点数，与上相同 %g 科学计数法(e)或浮点数 (根据显示长度) %G 科学计数法(E)或浮点数 (根据显示长度) %% 字符”%”  &gt;&gt;&gt; &#39;——%(p).
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%802/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E5%9F%BA%E4%BA%8Eface&#43;&#43;%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%9A%84%E6%8D%A2%E8%84%B8%E6%95%88%E6%9E%9C/">基于Face&#43;&#43;实现简单的换脸效果</a>
  </h1>
  <time datetime="2020-06-23T16:22:45&#43;0800" class="post-date">Tue, Jun 23, 2020</time>
  ​	Face++（旷视）通过提供云端API、离线SDK的自主研发产品形式，将人脸识别技术广泛应用到互联网及移动应用场景中，人脸识别云计算平台市场前景广阔。
前提 **1、我们先进入Face++网站，在控制台的应用管理选择 API Key ，并且创建一个 API Key **
2、安装了这三个包：
 requests simplejson json base64  3、找好两张图片，和修改好图片的存放位置
导包
import requests import simplejson import json import base64 创建获取人脸关键点函数
def find_face(imgpath): print(&#34;正在查找……&#34;) http_url = &#39;https://api-cn.faceplusplus.com/facepp/v3/detect&#39; data = {&#34;api_key&#34;: &#39;申请的API Key&#39;, &#34;api_secret&#34;: &#39;申请的API Secret&#39;, &#34;image_url&#34;: imgpath, &#34;return_landmark&#34;: 1} files = {&#34;image_file&#34;: open(imgpath, &#34;rb&#34;)} response = requests.post(http_url, data=data, files=files) req_con = response.content.decode(&#39;utf-8&#39;) req_dict = json.JSONDecoder().decode(req_con) this_json = simplejson.dumps(req_dict) this_json2 = simplejson.loads(this_json) print(this_json2) faces = this_json2[&#39;faces&#39;] list0 = faces[0] rectangle = list0[&#39;face_rectangle&#39;] #print(rectangle) return rectangle 开始换脸（照片大小最好小于2M，number为换脸的相似度）
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E5%9F%BA%E4%BA%8Eface&#43;&#43;%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%9A%84%E6%8D%A2%E8%84%B8%E6%95%88%E6%9E%9C/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%801/">Python基础1（列表、元组、内置函数）</a>
  </h1>
  <time datetime="2020-06-22T17:00:27&#43;0800" class="post-date">Mon, Jun 22, 2020</time>
  列表和元组 列表  list 可迭代类型 可变类型 有序类型
使用方括号来创建列表
li = [&#34;a&#34;, &#34;b&#34;, &#34;c&#34;, &#34;d&#34;]  索引和切片 &gt;&gt;&gt;li[0]&#39;a&#39;&gt;&gt;&gt;li[1:2][&#39;b&#39;] 与字符串切片功能不同之处：
 进行获取、切片、赋值给其他变量等 （getitem）得到的是其浅拷贝 进行直接赋值（setitem）则会改变原列表  getitem操作 a = [1,2]a[:1][0] = 5print(a)#打印结果[1,2] setitem操作 a = [1,2]a[:1] = [5]print(a)#打印结果[5,2] 关于切片
可以在学习面向对象的魔术方法后再深入研究
操作符 []+[] #拼合[]*int #重复item in [] #判断成员是否存在 返回布尔值 常用内置函数  len(list) 获取长度 list() 转化为列表类型 max()/min()求最值，可选参数key进行操作 reversed(list)临时翻转，不限于列表 sorted(iterable)临时排序，不限于列表 ，可选key和倒序。 sum所有成员是数字的时候可用，求和，不限于列表  常用方法  ''.join(list) 将列表转为字符串 .count(value)计次 .copy()浅拷贝，和切片[:]等效  增删改查  .append(p) 追加 .extend(iterable)迭代追加 .insert(index,obj)插入 .
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E5%9F%BA%E7%A1%801/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E7%88%AC%E8%99%AB%E7%88%AC%E5%8F%96%E7%99%BE%E5%BA%A6%E5%9B%BE%E7%89%87/">爬虫爬取百度图片</a>
  </h1>
  <time datetime="2020-06-21T17:20:34&#43;0800" class="post-date">Sun, Jun 21, 2020</time>
  from urllib.request import urlopen, urlretrieve import requests import re # url为要爬取图片的网址 url = &#34;http://image.baidu.com/search/index?tn=baiduimage&amp;ipn=r&amp;ct=201326592&amp;cl=2&amp;lm=-1&amp;st=-1&amp;fm=result&amp;fr=&amp;sf=1&amp;fmq=1580824135824_R&amp;pv=&amp;ic=0&amp;nc=1&amp;z=&amp;hd=&amp;latest=&amp;copyright=&amp;se=1&amp;showtab=0&amp;fb=0&amp;width=&amp;height=&amp;face=0&amp;istype=2&amp;ie=utf-8&amp;sid=&amp;word=%E9%82%B5%E5%B2%B1%E5%84%BF&#34; html = urlopen(url) html_ym = html.read().decode() urls = re.findall(r&#39;&#34;objURL&#34;:&#34;(.*?)&#34;&#39;,html_ym) index = 0 for url in urls: if index &lt;= 5: res = requests.get(url=url) with open(&#34;index&#34;+&#34;.jpg&#34;, &#39;wb&#39;) as f: f.write(res.content) print(index,&#34;已下载&#34;) index += 1 else: break 
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E8%A3%85%E9%A5%B0%E5%99%A8%E8%AE%A1%E7%AE%97%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4/">装饰器计算一个函数的运行时间</a>
  </h1>
  <time datetime="2020-06-18T18:01:48&#43;0800" class="post-date">Thu, Jun 18, 2020</time>
  import functools import time def run_time(fn): @functools.wraps(fn) def wrapper(*args, **kw): start = time.time() res = fn(*args, **kw) print(&#39;%s运行了 %f秒&#39; % (fn.__name__, time.time() - start)) return res return wrapper #测试 @run_time def test(n): time.sleep(n) print(&#34;运行结束了&#34;) test(3) 
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E4%BA%94%E7%A7%8Dio%E6%A8%A1%E5%9E%8B/">五种IO模型</a>
  </h1>
  <time datetime="2020-06-18T17:52:49&#43;0800" class="post-date">Thu, Jun 18, 2020</time>
  五种IO模型，分别是：阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。
1.阻塞IO模型 最传统的一种IO模型，即在读写数据过程中会发生阻塞现象。
　当用户线程发出IO请求之后，内核会去查看数据是否就绪，如果没有就绪就会等待数据就绪，而用户线程就会处于阻塞状态，用户线程交出CPU。当数据就绪之后，内核会将数据拷贝到用户线程，并返回结果给用户线程，用户线程才解除block状态。
典型的阻塞IO模型的例子为：
data = socket.read();
如果数据没有就绪，就会一直阻塞在read方法。
​	2.非阻塞IO模型 当用户线程发起一个read操作后，并不需要等待，而是马上就得到了一个结果。如果结果是一个error时，它就知道数据还没有准备好，于是它可以再次发送read操作。一旦内核中的数据准备好了，并且又再次收到了用户线程的请求，那么它马上就将数据拷贝到了用户线程，然后返回。
　所以事实上，在非阻塞IO模型中，用户线程需要不断地询问内核数据是否就绪，也就说非阻塞IO不会交出CPU，而会一直占用CPU。
典型的非阻塞IO模型一般如下：
while(true){ data = socket.read(); if(data!= error){ 处理数据 break; } } 　但是对于非阻塞IO就有一个非常严重的问题，在while循环中需要不断地去询问内核数据是否就绪，这样会导致CPU占用率非常高，因此一般情况下很少使用while循环这种方式来读取数据。
3.多路复用IO模型 　多路复用IO模型是目前使用得比较多的模型。Java NIO实际上就是多路复用IO。
　在多路复用IO模型中，会有一个线程不断去轮询多个socket的状态，只有当socket真正有读写事件时，才真正调用实际的IO读写操作。因为在多路复用IO模型中，只需要使用一个线程就可以管理多个socket，系统不需要建立新的进程或者线程，也不必维护这些线程和进程，并且只有在真正有socket读写事件进行时，才会使用IO资源，所以它大大减少了资源占用。
　在Java NIO中，是通过selector.select()去查询每个通道是否有到达事件，如果没有事件，则一直阻塞在那里，因此这种方式会导致用户线程的阻塞。
　也许有朋友会说，我可以采用多线程+ 阻塞IO 达到类似的效果，但是由于在多线程 + 阻塞IO 中，每个socket对应一个线程，这样会造成很大的资源占用，并且尤其是对于长连接来说，线程的资源一直不会释放，如果后面陆续有很多连接的话，就会造成性能上的瓶颈。
　而多路复用IO模式，通过一个线程就可以管理多个socket，只有当socket真正有读写事件发生才会占用资源来进行实际的读写操作。因此，多路复用IO比较适合连接数比较多的情况。
　另外多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中，不断地询问socket状态时通过用户线程去进行的，而在多路复用IO中，轮询每个socket状态是内核在进行的，这个效率要比用户线程要高的多。
　不过要注意的是，多路复用IO模型是通过轮询的方式来检测是否有事件到达，并且对到达的事件逐一进行响应。因此对于多路复用IO模型来说，一旦事件响应体很大，那么就会导致后续的事件迟迟得不到处理，并且会影响新的事件轮询。
4.信号驱动IO模型 　在信号驱动IO模型中，当用户线程发起一个IO请求操作，会给对应的socket注册一个信号函数，然后用户线程会继续执行，当内核数据就绪时会发送一个信号给用户线程，用户线程接收到信号之后，便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中，对TCP套接口几乎是没用的，原因是该信号产生得过于频繁，并且该信号的出现并没有告诉我们发生了什么事情
5.异步IO模型 　异步IO模型才是最理想的IO模型，在异步IO模型中，当用户线程发起read操作之后，立刻就可以开始去做其它的事。而另一方面，从内核的角度，当它受到一个asynchronous read之后，它会立刻返回，说明read请求已经成功发起了，因此不会对用户线程产生任何block。然后，内核会等待数据准备完成，然后将数据拷贝到用户线程，当这一切都完成之后，内核会给用户线程发送一个信号，告诉它read操作完成了。也就说用户线程完全不需要关心实际的整个IO操作是如何进行的，只需要先发起一个请求，当接收内核返回的成功信号时表示IO操作已经完成，可以直接去使用数据了。
　也就说在异步IO模型中，IO操作的两个阶段都不会阻塞用户线程，这两个阶段都是由内核自动完成，然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的，在信号驱动模型中，当用户线程接收到信号表示数据已经就绪，然后需要用户线程调用IO函数进行实际的读写操作；而在异步IO模型中，收到信号表示IO操作已经完成，不需要再在用户线程中调用iO函数进行实际的读写操作。
　注意，异步IO是需要操作系统的底层支持，在Java 7中，提供了Asynchronous IO。简称AIO
前面四种IO模型实际上都属于同步IO，只有最后一种是真正的异步IO，因为无论是多路复用IO还是信号驱动模型，IO操作的第2个阶段都会引起用户线程阻塞，也就是内核进行数据拷贝的过程都会让用户线程阻塞。
两种高性能IO设计模式 在传统的网络服务设计模式中，有两种比较经典的模式：
　一种是多线程，一种是线程池。
　对于多线程模式，也就说来了client，服务器就会新建一个线程来处理该client的读写事件，如下图所示：
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E4%BA%94%E7%A7%8Dio%E6%A8%A1%E5%9E%8B/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/">redis分布式锁</a>
  </h1>
  <time datetime="2020-06-17T16:23:42&#43;0800" class="post-date">Wed, Jun 17, 2020</time>
  1、选用Redis实现分布式锁原因：
（1）Redis有很高的性能； （2）Redis命令对此支持较好，实现起来比较方便
2、使用命令介绍：
（1）SETNX
SETNX key val：当且仅当key不存在时，set一个key为val的字符串，返回1；若key存在，则什么都不做，返回0。
（2）expire
expire key timeout：为key设置一个超时时间，单位为second，超过这个时间锁会自动释放，避免死锁。
（3）delete
delete key：删除key
在使用Redis实现分布式锁的时候，主要就会使用到这三个命令。
3、实现思想：
（1）获取锁的时候，使用setnx加锁，并使用expire命令为锁添加一个超时时间，超过该时间则自动释放锁，锁的value值为一个随机生成的UUID，通过此在释放锁的时候进行判断。
（2）获取锁的时候还设置一个获取的超时时间，若超过这个时间则放弃获取锁。
（3）释放锁的时候，通过UUID判断是不是该锁，若是该锁，则执行delete进行锁释放。
4、 分布式锁的简单实现代码：
#连接redis import time import uuid from threading import Thread import redis redis_client = redis.Redis(host=&#34;localhost&#34;, port=6379, # password=123, db=10) #获取一个锁 # lock_name：锁定名称 # acquire_time: 客户端等待获取锁的时间 # time_out: 锁的超时时间 def acquire_lock(lock_name, acquire_time=10, time_out=10): &#34;&#34;&#34;获取一个分布式锁&#34;&#34;&#34; identifier = str(uuid.uuid4()) end = time.time() + acquire_time lock = &#34;string:lock:&#34; + lock_name while time.time() &lt; end: if redis_client.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/redis%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E4%B9%90%E8%A7%82%E9%94%81/">redis乐观锁</a>
  </h1>
  <time datetime="2020-06-17T15:49:27&#43;0800" class="post-date">Wed, Jun 17, 2020</time>
  什么是乐观锁 乐观锁（ Optimistic Locking ） 相对悲观锁而言，乐观锁假设认为数据一般情况下不会造成冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误的信息，让用户决定如何去做。一般用于秒杀、抢购防止超卖这些功能中。
如何实现redis乐观锁 首先，看看redis的事务，涉及到的指令，主要有multi，exec，discard。而实现乐观锁的指令，在事务基础上，主要是watch指令，以及unwatch指令，unwatch通常可以不用！
1.multi，开启Redis的事务，置客户端为事务态。 2.exec，提交事务，执行从multi到此命令前的命令队列，置客户端为非事务态。 3.discard，取消事务，置客户端为非事务态。 4.watch,监视键值对，作用时如果事务提交exec时发现监视的监视对发生变化，事务将被取消。
案例1：redis的纯事务
下面是ssh窗口1里面的操作：
127.0.0.1:6379&gt; set hello 1 OK 127.0.0.1:6379&gt; get hello &#34;1&#34; 127.0.0.1:6379&gt; multi OK 127.0.0.1:6379&gt; incr hello QUEUED 127.0.0.1:6379&gt; incr hello #这一步执行完毕后，去另外一个窗口（ssh窗口2），对hello这个key做incr操作，将hello对应的值变成2。完成后，继续后面的exec指令 QUEUED 127.0.0.1:6379&gt; exec 1) (integer) 3 #注意，这时hello的值是3了，前面执行get hello指令时，值是1哟，说明这个值在其他地方被修改过，这里的其他地方，就是指前面提到的，在另外一个连接窗口里面执行的。 2) (integer) 4 127.0.0.1:6379&gt; 这个情景下，multi和exec之间的指令，依然是可以执行的。
下面的操作，就是在ssh窗口2里面的操作：
127.0.0.1:6379&gt; 127.0.0.1:6379&gt; get hello &#34;1&#34; 127.0.0.1:6379&gt; incr hello (integer) 2 127.0.0.1:6379&gt; 案例2： 利用watch指令，基于CAS机制，简单的乐观锁
下面是ssh窗口1里面的操作：
127.0.0.1:6379&gt; watch hello OK 127.0.0.1:6379&gt; get hello &#34;4&#34; 127.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/redis%E4%B9%90%E8%A7%82%E9%94%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E4%BA%8B%E5%8A%A1/">redis事务</a>
  </h1>
  <time datetime="2020-06-17T15:44:57&#43;0800" class="post-date">Wed, Jun 17, 2020</time>
  Redis事务的概念： ​	Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令，一个事务中所有命令都会被序列化。在事务执行过程，会按照顺序串行化执行队列中的命令，其他客户端提交的命令请求不会插入到事务执行命令序列中。
　总结说：redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis事务没有隔离级别的概念： 　批量操作在发送 EXEC 命令前被放入队列缓存，并不会被实际执行，也就不存在事务内的查询要看到事务里的更新，事务外查询不能看到。
Redis不保证原子性： 　Redis中，单条命令是原子性执行的，但事务不保证原子性，且没有回滚。事务中任意命令执行失败，其余的命令仍会被执行。
Redis事务的三个阶段：  开始事务 命令入队 执行事务  Redis事务相关命令： 　watch key1 key2 &hellip; : 监视一或多个key,如果在事务执行之前，被监视的key被其他命令改动，则事务被打断 （ 类似乐观锁 ）
　multi : 标记一个事务块的开始（ queued ）
　exec : 执行所有事务块的命令 （ 一旦执行exec后，之前加的监控锁都会被取消掉 ）　　discard : 取消事务，放弃事务块中的所有命令
　unwatch : 取消watch对所有key的监控
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/mysql%E4%BA%8B%E5%8A%A1%E5%92%8C%E9%9A%94%E7%A6%BB%E7%AD%89%E7%BA%A7/">mysql事务和隔离等级</a>
  </h1>
  <time datetime="2020-06-17T15:40:01&#43;0800" class="post-date">Wed, Jun 17, 2020</time>
  Mysql事务 MySQL 事务主要用于处理操作量大，复杂度高的数据。比如说，在人员管理系统中，你删除一个人员，你既需要删除人员的基本资料，也要删除和该人员相关的信息，如信箱，文章等等，这样，这些数据库操作语句就构成一个事务！
 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。 事务处理可以用来维护数据库的完整性，保证成批的 SQL 语句要么全部执行，要么全部不执行。 事务用来管理 insert,update,delete 语句  一般来说，事务是必须满足4个条件（ACID）：原子性（Atomicity，或称不可分割性）、一致性（Consistency）、隔离性（Isolation，又称独立性）、持久性（Durability）。
 原子性：一个事务（transaction）中的所有操作，要么全部完成，要么全部不完成，不会结束在中间某个环节。事务在执行过程中发生错误，会被回滚（Rollback）到事务开始前的状态，就像这个事务从来没有执行过一样。 一致性：在事务开始之前和事务结束以后，数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则，这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。 数据库允许多个并发事务同时对其数据进行读写和修改的能力，隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 事务隔离分为不同级别，包括读未提交（Read uncommitted）、读提交（read committed）、可重复读（repeatable read）和串行化（Serializable）。 事务处理结束后，对数据的修改就是永久的，即便系统故障也不会丢失。  Mysql事务隔离等级   Read uncommitted 读未提交，顾名思义，就是一个事务可以读取另一个未提交事务的数据。事例：老板要给程序员发工资，程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字，按成3.9万/月，该钱已经打到程序员的户口，但是事务还没有提交，就在这时，程序员去查看自己这个月的工资，发现比往常多了3千元，以为涨工资了非常高兴。但是老板及时发现了不对，马上回滚差点就提交了的事务，将数字改成3.6万再提交。分析：实际程序员这个月的工资还是3.6万，但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
  Read committed 读提交，顾名思义，就是一个事务要等另一个事务提交后才能读取数据。事例：程序员拿着信用卡去享受生活（卡里当然是只有3.6万），当他埋单时（程序员事务开启），收费系统事先检测到他的卡里有3.6万，就在这个时候！！程序员的妻子要把钱全部转出充当家用，并提交。当收费系统准备扣款时，再检测卡里的金额，发现已经没钱了（第二次检测金额当然要等待妻子转出金额事务提交完）。程序员就会很郁闷，明明卡里是有钱的…分析：这就是读提交，若有事务对数据进行更新（UPDATE）操作时，读操作事务要等待这个更新操作事务提交后才能读取数据，可以解决脏读问题。但在这个事例中，出现了一个事务范围内两个相同的查询却返回了不同数据，这就是不可重复读。
  Repeatable read 重复读，就是在开始读取数据（事务开启）时，不再允许修改操作事例：程序员拿着信用卡去享受生活（卡里当然是只有3.6万），当他埋单时（事务开启，不允许其他事务的UPDATE修改操作），收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。分析：重复读可以解决不可重复读问题。写到这里，应该明白的一点就是，不可重复读对应的是修改，即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作，而不是UPDATE操作。
  Serializable 序列化 Serializable 是最高的事务隔离级别，在该级别下，事务串行化顺序执行，可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下，比较耗数据库性能，一般不使用。什么时候会出现幻读？事例：程序员某一天去消费，花了2千元，然后他的妻子去查看他今天的消费记录（全表扫描FTS，妻子事务开启），看到确实是花了2千元，就在这个时候，程序员花了1万买了一部电脑，即新增INSERT了一条消费记录，并提交。当妻子打印程序员的消费记录清单时（妻子事务提交），发现花了1.2万元，似乎出现了幻觉，这就是幻读。序列化解决幻读。
  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/nginx%E7%9A%84%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86%E5%92%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/">nginx的正向代理和反向代理、负载均衡</a>
  </h1>
  <time datetime="2020-06-17T14:40:20&#43;0800" class="post-date">Wed, Jun 17, 2020</time>
  nginx概述 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器；同时也是一个IMAP、POP3、SMTP代理服务器；nginx可以作为一个HTTP服务器进行网站的发布处理，另外nginx可以作为反向代理进行负载均衡的实现。
这里主要通过三个方面简单介绍nginx
 反向代理 负载均衡 nginx特点  正向代理 正向代理最大的特点是客户端非常明确要访问的服务器地址；服务器只清楚请求来自哪个代理服务器，而不清楚来自哪个具体的客户端；正向代理模式屏蔽或者隐藏了真实客户端信息。
我们都知道我们国家的互联网是有一道防火墙的，如果我们工作中因为种种原因要访问国外的网站，我们就会发现访问不到。这个时候我们我们连上一个国外VPN就能访问国外的网站了。VPN就是代理服务器。这种代理模式就是正向代理。
反向代理 通过上述的图解大家就可以看清楚了，多个客户端给服务器发送的请求，nginx服务器接收到之后，按照一定的规则分发给了后端的业务处理服务器进行处理了。此时~请求的来源也就是客户端是明确的，但是请求具体由哪台服务器处理的并不明确了，nginx扮演的就是一个反向代理角色
反向代理，主要用于服务器集群分布式部署的情况下，反向代理隐藏了服务器的信息！
项目场景
通常情况下，我们在实际项目操作时，正向代理和反向代理很有可能会存在在一个应用场景中，正向代理代理客户端的请求去访问目标服务器，目标服务器是一个反向单利服务器，反向代理了多台真实的业务处理服务器。
负载均衡 nginx支持的负载均衡调度算法方式如下：
 weight轮询（默认）：接收到的请求按照顺序逐一分配到不同的后端服务器，即使在使用过程中，某一台后端服务器宕机，nginx会自动将该服务器剔除出队列，请求受理情况不会受到任何影响。 这种方式下，可以给不同的后端服务器设置一个权重值（weight），用于调整不同的服务器上请求的分配率；权重数据越大，被分配到请求的几率越大；该权重值，主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的。 权重(weight) ：指定轮询几率，weight和访问比率成正比，用于后端服务器性能不均的情况。 ip_hash（ip绑定）：每个请求按照发起客户端的ip的hash结果进行匹配，这样的算法下一个固定ip地址的客户端总会访问到同一个后端服务器，这也在一定程度上解决了集群部署环境下session共享的问题。 fair（第三方插件）：智能调整调度算法，动态的根据后端服务器的请求处理到响应的时间进行均衡分配，响应时间短处理效率高的服务器分配到请求的概率高，响应时间长处理效率低的服务器分配到的请求少；结合了前两者的优点的一种调度算法。但是需要注意的是nginx默认不支持fair算法，如果要使用这种调度算法，请安装upstream_fair模块。（按后端服务器相应速度来分配请求，响应时间短的优先分配） url_hash（url绑定）：按照访问的url的hash结果分配请求，每个请求的url会指向后端固定的某个服务器，可以在nginx作为静态服务器的情况下提高缓存效率。同样要注意nginx默认不支持这种调度算法，要使用的话需要安装nginx的hash软件包  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E4%BA%8C%E5%8F%89%E6%A0%91/">Python数据结构——二叉树</a>
  </h1>
  <time datetime="2020-06-09T18:01:55&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  1. 二叉树 二叉树(binary tree)中的每个节点都不能有多于两个的儿子。
1.1 二叉树列表实现
如上图的二叉树可用列表表示：
tree=[&#39;A&#39;, #root [&#39;B&#39;, #左子树 [&#39;D&#39;,[],[]], [&#39;E&#39;,[],[]]], [&#39;C&#39;, #右子树 [&#39;F&#39;,[],[]], []] ] 实现：
def BinaryTree(item): return [item,[],[]] def insertLeft(tree,item): leftSubtree=tree.pop(1) if leftSubtree: tree.insert(1,[item,leftSubtree,[]]) else: tree.insert(1,[item,[],[]]) return tree def insertRight(tree,item): rightSubtree=tree.pop(2) if rightSubtree: tree.insert(2,[item,[],rightSubtree]) else: tree.insert(2,[item,[],[]]) return tree def getLeftChild(tree): return tree[1] def getRightChild(tree): return tree[2] 要实现下图的树：
　tree=BinaryTree(&#39;a&#39;) insertLeft(tree,&#39;b&#39;) insertRight(tree,&#39;c&#39;) insertRight((getLeftChild(tree)),&#39;d&#39;) insertLeft((getRightChild(tree)),&#39;e&#39;) insertRight((getRightChild(tree)),&#39;f&#39;) 1.2 二叉树的类实现
class BinaryTree(object): def __init__(self,item): self.key=item self.leftChild=None self.rightChild=None def insertLeft(self,item): if self.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E4%BA%8C%E5%8F%89%E6%A0%91/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%95%A3%E5%88%97%E8%A1%A8%E5%93%88%E5%B8%8C%E8%A1%A8/">python数据结构----散列表(哈希表)</a>
  </h1>
  <time datetime="2020-06-09T17:55:07&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  [Python数据结构——散列表] 散列表的实现常常叫做散列(hashing)。散列仅支持INSERT,SEARCH和DELETE操作，都是在常数平均时间执行的。需要元素间任何排序信息的操作将不会得到有效的支持。
散列表是普通数组概念的推广。如果空间允许，可以提供一个数组，为每个可能的关键字保留一个位置，就可以运用直接寻址技术。
当实际存储的关键字比可能的关键字总数较小时，采用散列表就比较直接寻址更为有效。在散列表中，不是直接把关键字用作数组下标，而是根据关键字计算出下标，这种
关键字与下标之间的映射就叫做散列函数。
1.散列函数 一个好的散列函数应满足简单移植散列的假设：每个关键字都等可能的散列到m个槽位的任何一个中去，并与其它的关键字已被散列到哪个槽位无关。
1.1 通常散列表的关键字都是自然数。
1.11 除法散列法
通过关键字k除以槽位m的余数来映射到某个槽位中。
hash(k)=k mod m 应用除法散列时，应注意m的选择，m不应该是2的幂，通常选择与2的幂不太接近的质数。
1.12 乘法散列法
乘法方法包含两个步骤，第一步用关键字k乘上常数A(0&lt;A&lt;1),并取出小数部分，然后用m乘以这个值，再取结果的底(floor)。
hash(k)=floor(m(kA mod 1)) 乘法的一个优点是对m的选择没有什么特别的要求，一般选择它为2的某个幂。
一般取A=(√5-1)/2=0.618比较理想。
1.13 全域散列
随机的选择散列函数，使之独立于要存储的关键字。在执行开始时，就从一族仔细设计的函数中，随机的选择一个作为散列函数，随机化保证了
没有哪一种输入会始终导致最坏情况发生。
1.2 如果关键字是字符串，散列函数需要仔细的选择
1.2.1 将字符串中字符的ASCII码值相加
def _hash(key,m): hashVal=0 for _ in key: hashVal+=ord(_) return hashVal%m 由于ascii码最大127，当表很大时，函数不会很好的分配关键字。
1.2.2 取关键字的前三个字符。
值27表示英文字母表的字母个数加上一个空格。
hash(k)=k[0]+27*k[1]+729*k[2] 1.2.3 用霍纳法则把所有字符扩展到n次多项式。
用32代替27，可以用于位运算。
def _hash(key,m): hashval=0 for _ in key: hashval=(hashval&lt;&lt;5)+ord(_) return hashval%m 2. 分离链接法 散列表会面临一个问题，当两个关键字散列到同一个值的时候，称之为冲突或者碰撞(collision)。解决冲突的第一种方法通常叫做分离链接法(separate chaining)。
其做法是将散列到同一个值的所有元素保留到一个链表中，槽中保留一个指向链表头的指针。
为执行FIND，使用散列函数来确定要考察哪个表，遍历该表并返回关键字所在的位置。
为执行INSERT，首先确定该元素是否在表中。如果是新元素，插入表的前端或末尾。
为执行DELETE，找到该元素执行链表删除即可。
散列表中元素个数与散列表大小的比值称之为装填因子(load factor)λ。
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%95%A3%E5%88%97%E8%A1%A8%E5%93%88%E5%B8%8C%E8%A1%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E8%B7%B3%E8%A1%A8/">python数据结构----跳表</a>
  </h1>
  <time datetime="2020-06-09T17:32:43&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  链表 同数组比较，链表的插入删除操作复杂度是O(1)，然而如果想要查找一个节点，你可能会这么写：
LinkedNode&lt;T&gt; *targetNode = headerNode; while(targetNode) { if(targetNode-&gt;element == theElement) return targetNode; targetNode = targetNode-&gt;next; } return NULL; 你会发现每次都是从链表头节点开始，时间复杂度是n（节点个数）。为了提高算法速度，就有了跳表的概念。
跳表 结合链表和二分法的特点，将链表进行加工，创造一个二者的结合体： 1.链表从头节点到尾节点是有序的 2.可以进行跳跃查找（形如二分法）
next数组 实现第一步，只需在插入的时候进行有序插入即可，核心步骤在第二步，构建一个可以实现在节点之间跳跃的链。 对于普通链表而言，你的节点也许会长这个样子（或者再多个构造析构函数？）:
template&lt;class T&gt; class linkedNode { public: T element; linkedNode&lt;T&gt; *next; }; 这里的next指针是我们用于将此节点同后面一个节点连接的桥梁，正如你知道的那样，next是下一个节点的指针。
需要你考虑的事情来了，如果需要在不挨着的节点之间实现跳转，那么对于某一个节点来说，它就需要存储一个以上的指向别的节点的next指针。多个同种类型的数据通常可以采用数组存储（vector, list等STL容器也可以啦），这样，我们的跳表节点或许大概差不多应该长这个样子：
template&lt;class T&gt; class skipNode { public: T element; skipNode&lt;T&gt; **next; //一个存储着skipNode&lt;T&gt;*类型变量的一维数组 }; 暂且不论next数组的大小是多少，先让我告诉你跳表的样子。
你可能会觉得跳表这个东西很奇怪，不过最好不是因为我画的比较丑。先不管竖着的线是什么，横着看每一层好像都是一个链表，对吗。另外我觉得你也应该注意一下竖着看的时候每一个节点的名字，它们也都是一样的（不用考虑为什么有的节点有一个，有的有两个甚至更多，这是插入函数应该解决的问题）。
所以我想你现在脑子中可能有了对next数组的一种假设，尽管事实可能并不是你想的那样，不过不要紧，让我借着这张图告诉你next数组是什么。
通常我们会定义跳表的级数，简单来解释，就是层数-1（最下面一级是0级）。所以这张图表示的跳表的级数是（0， 1， 2）。而在第2级的节点（比如node5)，它的next数组大小就是(2 + 1) == 3， 在第1级的节点（比如node4），它的next数组的大小就是（1 + 1） == 2， 在第0级的节点（比如node3），它的next数组大小是1；
所以在申请一个skipNode的指针时，传入这个节点所在的级数好像是理所应当的事情，然后对next进行初始化，把skipNode加工一下，像这样：
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E8%B7%B3%E8%A1%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97/">Python数据结构----栈和队列</a>
  </h1>
  <time datetime="2020-06-09T17:22:10&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  栈：先进先出
队列：先进后出
队列
首先，我们来定义一个队列类：
class Queue(): def __init__(self): self.__list = list() 接下来，我们给队列类添加一些方法：
•判断队列是否为空
def isEmpty(self): return self.__list == [] •入队
def push(self, data): self.__list.append(data) •出队
def pop(self): if self.isEmpty(): return False return self.__list.pop(0) •定义len()函数和print()操作类方法
def __len__(self): return len(self.__list) def __str__(self): if self.isEmpty(): return &#39;&#39; return &#39; &#39;.join([str(x) for x in self.__list]) 栈
栈的实现和队列类似，同样有入栈和出栈操作
class Stack(): def __init__(self): self.__list = list() def isEmpty(self): return self.__list == [] def push(self, data): self.__list.append(data) def pop(self): if self.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E9%93%BE%E8%A1%A8/">Python数据结构----链表</a>
  </h1>
  <time datetime="2020-06-09T17:00:26&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  一、链表简介 链表是一种在存储单元上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过链表中的指针链接次序实现。链表是由一系列的结点组成，结点可以在运行时动态生成。每个结点包含两部分：数据域与指针域。数据域存储数据元素，指针域存储下一结点的指针。
二、单向链表 单向链表也叫单链表，是链表中最简单的形式，它的每个节点包含两个域，一个信息域（元素域）和一个链接域。这个链接指向链表中的下一个节点，而最后一个节点的链接域则指向一个空值。
head 保存首地址，item 存储数据，next 指向下一结点地址。
链表失去了序列的随机读取优点，同时链表增加了指针域，空间开销也较大，但它对存储空间的使用要相对灵活。
列如：有一堆数据[1,2,3,5,6,7]，要在3和5之间插入4, 如果用数组，需要将5之后的数据都往后退一位，然后再插入4，这样非常麻烦，但是如果用链表，我就直接在3和5之间插入4就行。
1. 定义结点 结点的数据结构为数据元素(item)与 指针(next)
class Node(object): &#34;&#34;&#34;单链表的结点&#34;&#34;&#34; def __init__(self, item): # item存放数据元素 self.item = item # next是下一个节点的标识 self.next = None 2. 定义链表 链表需要具有首地址指针head。
class SingleLinkList(object): &#34;&#34;&#34;单链表&#34;&#34;&#34; def __init__(self): self._head = None 示例：创建链表
if __name__ == &#39;__main__&#39;: # 创建链表 link_list = SingleLinkList() # 创建结点 node1 = Node(1) node2 = Node(2) # 将结点添加到链表 link_list._head = node1 # 将第一个结点的next指针指向下一结点 node1.next = node2 # 访问链表 print(link_list.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E9%93%BE%E8%A1%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%95%B0%E7%BB%84/">python数据结构----数组</a>
  </h1>
  <time datetime="2020-06-09T16:45:05&#43;0800" class="post-date">Tue, Jun 9, 2020</time>
  1、一维数组Array类使用python列表来保存其项，常用的魔法方法包括初始化(init)、求其长度(len)、字符串表示(str)、迭代(iter)、获取指定位置的值(getitem)、设置指定位置的值(setitem)
数组的操作主要有：增加数组的长度、减小数组的长度、在数组中插入新的一项、从数组中删除一项。
   增加数组大小：如果逻辑大小和物理大小相等，此时就要增加物理大小，在时间复杂和空间复杂度中折衷中，我们选择将物理大小调整为原来的两倍，然后创建一个新的数组，并且将旧的数组中的值拷贝到新的数组当中，最后将旧的数组变量重置为新的数组变量。     减小数组大小：条件为：装载因子要小于预设的值(如0.25)同时现在数组的物理大小要为默认值的两倍以上。装载因子为逻辑大小和物理大小的比值。如果满足条件，将物理大小减少一半，后续步骤和增加数组的步骤一样。     在数组中插入新的一项：在插入之前要先判断是否有可用空间，其次从数组的末尾开始直至目标位置，将每一项后移一个单元，然后见空余的目标位置填入新的值，最后将逻辑大小加1     在数组中删除一项：从紧跟目标索引位置之后的位置开始，到数组的逻辑末尾，将每一项都向前移动一位；然后将逻辑大小减1；最后判断是否需要减小数组大小。    Code:
class Array(object): def __init__(self, capacity, fillValue=None): self.items= list() for i in range(capacity): self.items.append(fillValue) def __len__(self): &#34;&#34;&#34;len of the array&#34;&#34;&#34; return len(self.items) def __str__(self): &#34;&#34;&#34;string representation of thearray&#34;&#34;&#34; return str(self.items) def __iter__(self): &#34;&#34;&#34;iter of thr array&#34;&#34;&#34; return iter(self.items) def __getitem__(self, index): &#34;&#34;&#34;get item of the array atindex&#34;&#34;&#34; return self.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E6%95%B0%E7%BB%84/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/python%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/">python垃圾回收机制</a>
  </h1>
  <time datetime="2020-06-08T18:45:30&#43;0800" class="post-date">Mon, Jun 8, 2020</time>
  什么是内存管理和垃圾回收一 、什么是内存管理和垃圾回收 Python GC主要使用引用计数（reference counting）来跟踪和回收垃圾。在引用计数的基础上，通过“标记-清除”（mark and sweep）解决容器对象可能产生的循环引用问题，通过“分代回收”（generation collection）以空间换时间的方法提高垃圾回收效率。
现在的高级语言如java，c#等，都采用了垃圾收集机制，而不再是c，c++里用户自己管理维护内存的方式。自己管理内存极其自由，可以任意申请内存，但如同一把双刃剑，为大量内存泄露，悬空指针等bug埋下隐患。 对于一个字符串、列表、类甚至数值都是对象，且定位简单易用的语言，自然不会让用户去处理如何分配回收内存的问题。 python里也同java一样采用了垃圾收集机制，不过不一样的是:
最关键的一句话：
python采用的是引用计数机制为主，标记-清除和分代收集两种机制为辅的策略一、引用计数——reference countPyObject是每个对象必有的内容，其中ob_refcnt就是做为引用计数。当一个对象有新的引用时，它的ob_refcnt就会增加，当引用它的对象被删除，它的ob_refcnt就会减少.引用计数为0时，该对象生命就结束了。
优点:
 简单 实时性  缺点:
那么关键问题来了，什么时候，引用计数增加1，什么时候引用计数减少1呢？
我们可以通过使用内置的模块sys.getrefcount(a)可以查看a对象的引用计数，但是比正常计数大1，因为调用函数的时候传入a，这会让a的引用计数+1。
1、简单实例： 维护引用计数消耗资源 循环引用——最致命的缺点（后面会定义） 导致引用计数+1的情况 对象被创建，例如a=23 对象被引用，例如b=a 对象被作为参数，传入到一个函数中，例如func(a) 对象作为一个元素，存储在容器中，例如list1=[a,a]   导致引用计数-1的情况 对象的别名被显式销毁，例如del a 对象的别名被赋予新的对象，例如a=24 一个对象离开它的作用域，例如f函数执行完毕时，func函数中的局部变量（全局变量不会） 对象所在的容器被销毁，或从容器中删除对象    import sys class A: pass def func(x): print(f&#39;对象a：{sys.getrefcount(x)-1}&#39;,end=&#39; &#39;) return x #a=123.56 a=A() #创建对象a print(f&#39;对象a：{sys.getrefcount(a)-1}&#39;) b=a #再一次引用对象a print(f&#39;对象a：{sys.getrefcount(a)-1}，对象b：{sys.getrefcount(b)-1}&#39;) c=func(a) #对象a作为函数参数 print(f&#39;对象c：{sys.getrefcount(c)-1}&#39;) d=list() #对象a作为列表元素 d.append(a) print(f&#39;对象a：{sys.getrefcount(a)-1}，对象d：{sys.getrefcount(d)-1}&#39;) 运行结果为：
对象a：1
对象a：2，对象b：2
  
  <div class="read-more-link">
    <a href="/my_technology_blog/python%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/gil/">GIL</a>
  </h1>
  <time datetime="2020-06-08T18:43:42&#43;0800" class="post-date">Mon, Jun 8, 2020</time>
  保证同一时刻只有一个线程，为了解决线程相互竞争资源
熟悉python的都知道，在C语言写的python解释器中存在全局解释器锁，由于全局解释器锁的存在，在同一时间内，python解释器只能运行一个线程的代码，这大大影响了python多线程的性能。而这个解释器锁由于历史原因，现在几乎无法消除。
python GIL 之所以会影响多线程等性能，是因为在多线程的情况下，只有当线程获得了一个全局锁的时候，那么该线程的代码才能运行，而全局锁只有一个，所以使用python多线程，在同一时刻也只有一个线程在运行，因此在即使在多核的情况下也只能发挥出单核的性能。
既然python在同一时刻下只能运行一个线程的代码，那线程之间是如何调度的呢？
对于有io操作的线程，当一个线程在做io操作的时候，因为io操作不需要cpu，所以，这个时候，python会释放python全局锁，这样其他需要运行的线程就会使用该锁。
对于cpu密集型的线程，比如一个线程可能一直需要使用cpu做计算，那么python中会有一个执行指令的计数器，当一个线程执行了一定数量的指令时，该线程就会停止执行并让出当前的锁，这样其他的线程就可以执行代码了。
由上面可知，至少有两种情况python会做线程切换，一是一但有IO操作时，会有线程切换，二是当一个线程连续执行了一定数量的指令时，会出现线程切换。当然此处的线程切换不一定就一定会切换到其他线程执行，因为如果当前线程 优先级比较高的话，可能在让出锁以后，又继续获得锁，并优先执行。
在做科学计算的时候是用的单线程，因为这种计算是需要CPU一直做计算的，如果用多线程反而会降低计算速度。
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/webshell%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%B5%81%E7%A8%8B/">webshell的实现流程</a>
  </h1>
  <time datetime="2020-06-02T16:31:24&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  什么是webshell ​	“web”的含义是显然需要服务器开放web服务，“shell”的含义是取得对服务器某种程度上操作权限。webshell常常被称为通过网站端口对网站服务器的某种程度上操作的权限。
vue端
1、安装
//指定版本安装，最新版的xterm文件的改动很大，使用下面的方法会报错 npm install xterm@3.1.0 --save 2、main中导包
import &#39;xterm/dist/xterm.css&#39;; import { Terminal } from &#39;xterm&#39;; // 布局组件 可以不用 import * as fit from &#39;xterm/lib/addons/fit/fit&#39;; Terminal.applyAddon(fit); 3、在页面中的显示
&lt;template&gt; &lt;div&gt; &lt;div id=&#34;terminal&#34; style=&#34;width: 500px;height:300px;&#34;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/template&gt; 4.数据操作
mounted () { //实例化一个websocket，用于和django江湖  this.terminalSocket = new WebSocket(&#34;ws://127.0.0.1:8000/web/&#34;); //获取到后端传回的信息  this.terminalSocket.onmessage = (res) =&gt; { console.log(res.data); // var message = JSON.parse(res.data);  //将传回来的数据显示在xterm里  this.term.writeln(&#34;\r\n&#34;+res.data); //重置要发送的信息  this.order = &#34;&#34; //换行，显示下一个开头  this.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/webshell%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%B5%81%E7%A8%8B/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E7%82%B9%E5%AF%B9%E7%82%B9%E5%92%8C%E7%BE%A4%E5%8F%91%E7%9A%84%E6%B5%81%E7%A8%8B/">点对点和群发的流程</a>
  </h1>
  <time datetime="2020-06-02T16:30:33&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  点对点如何通行？  存储所有的连接 消息中指定接收人和发送人  群发如何实现？  保存所有连接 连接全部转发   
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/websocket%E8%A7%A3%E5%86%B3%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8/">websocket解决什么问题？如何使用？</a>
  </h1>
  <time datetime="2020-06-02T15:50:54&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  WebSocket使得客户端和服务器之间的数据交换变得更加简单，允许服务端主动向客户端推送数据。在WebSocket API中，浏览器和服务器只需要完成一次握手，两者之间就直接可以创建持久性的连接，并进行双向数据传输
使用
django端
# 导入dwebsocket的库 from dwebsocket.decorators import accept_websocket @accept_websocket def websocket_link(request): # 定义一个独立的用户id userid = str(uuid.uuid1()) # 循环监听 while True: # 获取连接信息 message = request.websocket.wait() # 如果没有链接信息，断开链接 if not message: break else: print(&#39;连接成功&#39; + str(message, encoding=&#34;utf-8&#34;)) # 针对指定用户，建立链接 clients[userid] = request.websocket # 定义通过websocket发送消息 def websocket_send(request): # 获取消息内容 msg = request.GET.get(&#34;msg&#34;) # 遍历客户端列表，推送消息 for client in clients: # 推送 clients[client].send(msg.encode(&#39;utf-8&#39;)) return HttpResponse({&#34;msg&#34;: &#34;推送消息成功&#34;}) vue端
&lt;template&gt; &lt;div&gt; &lt;mh&gt;&lt;/mh&gt; &lt;main class=&#34;&#34; id=&#34;main-collapse&#34;&gt; &lt;div&gt; &lt;!
  
  <div class="read-more-link">
    <a href="/my_technology_blog/websocket%E8%A7%A3%E5%86%B3%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E9%80%92%E5%BD%92%E7%9A%84%E6%84%8F%E4%B9%89%E4%BC%98%E5%8A%A3%E5%8A%BF%E5%BF%85%E8%A6%81%E6%9D%A1%E4%BB%B6/">递归的意义，优劣势，必要条件</a>
  </h1>
  <time datetime="2020-06-02T15:45:06&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  1、递归的意义
递归能把问题分解成为规模更小的、具有与原问题有着相同解法的问题
2、递归的优劣势
优势
 代码简洁 符合思维习惯，容易理解  劣势
 效率较低 递归层次太深，耗内存且容易栈溢出一定要使用的话，最好使用缓存避免相同的计算，限制递归调用的次数  3、递归的必要条件
 明确函数要做什么 寻找递归结束条件 找出函数的等价关系式  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/django%E7%9A%84%E9%80%92%E5%BD%92%E6%B7%B1%E5%BA%A6%E6%98%AF%E5%A4%9A%E5%B0%91%E5%A6%82%E4%BD%95%E6%94%B9%E5%8F%98%E9%80%92%E5%BD%92%E6%B7%B1%E5%BA%A6/">django的递归深度是多少？如何改变递归深度？</a>
  </h1>
  <time datetime="2020-06-02T15:41:16&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  django的递归深度是998
改变最大递归深度
import sys sys.setrecursionlimit(1000000) # 传入你想要设置的递归的最大深度的值就行了 
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%97%A0%E9%99%90%E6%9E%81%E8%AF%84%E8%AE%BA/">无限极评论</a>
  </h1>
  <time datetime="2020-06-02T10:44:56&#43;0800" class="post-date">Tue, Jun 2, 2020</time>
  vue父子组件的使用
//父组件 &lt;template&gt; &lt;div&gt; // 使用子组件，将子组件显示在这里  &lt;zi :data=&#34;data&#34; /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import zi from &#39;./zi&#39;	// 将子组件导入父组件 import axios from &#39;axios&#39; export default { name:&#34;fuzujian&#34;, data() { return { data:&#39;&#39; } }, //注册子组件  components:{ zi }, methods:{ get_info(){ axios.get(&#34;http://127.0.0.1:8000/comment/com/&#34;).then(res=&gt;{ var mytree = {&#34;id&#34;:0,&#34;content&#34;:&#34;&#34;} mytree[&#34;childlist&#34;] = res.data.data this.data= mytree }) } }, created(){ this.get_info() } } &lt;/script&gt; //子组件 &lt;template&gt; &lt;div&gt; &lt;li&gt; &lt;ul &gt; 看数据后方可精神崩溃的解放后卡加斯那可就发你 &lt;/ul&gt; &lt;/li&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name:&#34;zi&#34; } &lt;/script&gt; &lt;style&gt; ul{ list-style: none; /* padding-left: 20px */ } &lt;/style&gt; 父组件给子组件传参
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E6%97%A0%E9%99%90%E6%9E%81%E8%AF%84%E8%AE%BA/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E4%B8%83%E7%89%9B%E4%BA%91/">七牛云</a>
  </h1>
  <time datetime="2020-06-01T11:32:40&#43;0800" class="post-date">Mon, Jun 1, 2020</time>
  1、七牛云上传django端
# 导包 from qiniu import Auth # 定义七牛云存储接口 class QiNiu(APIView): def get(self,request): # 定义密钥 q = Auth(&#39;5Veq6sNOg8ka1d9uIeOS3Wazb7AESwvrBbCsdJWi&#39;, &#39;_8g1em1f0XJ-Ge4AoxZhNSc3p1HnqvhqFuC6UNcJ&#39;) # 指定上传空间 token = q.upload_token(&#39;1906md&#39;) print(token) res = {} res[&#39;token&#39;] = token return Response(res) 2、vue端template中上传框定义
&lt;tr&gt; &lt;td style=&#34;padding:5px;&#34;&gt; 七牛云上传: &lt;/td&gt; &lt;td style=&#34;padding:5px;&#34;&gt; &lt;input type=&#34;file&#34; @change=&#34;upload_qiniu&#34; /&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td style=&#34;padding:5px;&#34;&gt; 进度展示: &lt;/td&gt; &lt;td style=&#34;padding:5px;&#34;&gt; {{ loadpercent }} &lt;Progress v-show=&#34;loadpercent_int&#34; :percent=&#34;loadpercent_int&#34; color=&#34;blue&#34;&gt;&lt;span slot=&#34;title&#34;&gt;&lt;/span&gt;&lt;span slot=&#34;text&#34;&gt;{{ loadpercent_int }}%&lt;/span&gt;&lt;/Progress&gt; &lt;/td&gt; &lt;/tr&gt; 3、data中定义七牛云上传凭证
// 七牛上传凭证 uptoken:&#34;&#34;, // 进度 loadpercent:&#39;&#39;, 4、methods中获取七牛云凭证
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E4%B8%83%E7%89%9B%E4%BA%91/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81/">滑块验证码</a>
  </h1>
  <time datetime="2020-06-01T11:00:38&#43;0800" class="post-date">Mon, Jun 1, 2020</time>
  安装插件
npm install vue-drag-verify --save 1、在使用的vue页面导入
import dragVerify from &#39;vue-drag-verify&#39;; 2、注册组件
//注册组件标签,在export default中  components:{ &#39;dragVerify&#39;:dragVerify, &#39;myheader&#39;:myheader }, 3、在template中使用滑块
&lt;drag-verify :width=&#34;width&#34; :height=&#34;height&#34; :text=&#34;text&#34; ref=&#34;Verify&#34; &gt; &lt;/drag-verify&gt; 4、data中定义所使用的变量
//滑块验证码，width和height根据实际滑块定义 text:&#39;请将滑块拖动到最右侧&#39;, width:300, height:40, 5、提交时进行判断
//判断是否拖动滑块 console.log(this.$refs.Verify.isPassing); if(this.$refs.Verify.isPassing==false){ this.$Message(&#39;请滑动验证&#39;); return false; } 
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/vue%E4%B8%8B%E8%BD%BDexcel%E6%95%B0%E6%8D%AE/">vue下载excel数据</a>
  </h1>
  <time datetime="2020-05-29T17:08:35&#43;0800" class="post-date">Fri, May 29, 2020</time>
  ###vue下载excel数据
1、安装依赖包
1、npm install -S file-saver 2、npm install -S xlsx 3、npm install -D script-loader 2、在src目录下创建一个excel的文件夹
​	2、1:在目录下创建Blob.js
/* eslint-disable */ /* Blob.js*/ /*global self, unescape */ /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, plusplus: true */ /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ (function (view) { &#34;use strict&#34;; view.URL = view.URL || view.webkitURL; if (view.Blob &amp;&amp; view.URL) { try { new Blob; return; } catch (e) { } } // Internally we use a BlobBuilder implementation to base Blob off of  // in order to support older browsers that only have BlobBuilder  var BlobBuilder = view.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/vue%E4%B8%8B%E8%BD%BDexcel%E6%95%B0%E6%8D%AE/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%8A%98%E7%BA%BF%E5%9B%BE/">dajngo&#43;vue折线图的绘制</a>
  </h1>
  <time datetime="2020-05-29T16:42:12&#43;0800" class="post-date">Fri, May 29, 2020</time>
  dajngo+vue折线图的绘制 利用v-charts
进入v-charts官网
1、npm安装
npm i v-charts echarts -S 2、main中引入v-charts
// v-chart表格 import VCharts from &#39;v-charts&#39; Vue.use(VCharts) 3、前端vue
// data chartData: { // columns为横纵坐标  columns: [&#39;time&#39;, &#39;wen&#39;], rows: [] } // methods // 接受传过来的数据，goodslist.blist后台传过来的数据，类型不许为list套dict,并且所有温度的key要相同，所有时间的key也要相同 //后端数据库数据列表类型还原用eval()函数 this.chartData.rows = resp.goodslist.blist; 数据类型展示
[{&#39;wen&#39;: 21, &#39;time&#39;: &#39;2020-05-29 09:46:57&#39;}, {&#39;wen&#39;: -28, &#39;time&#39;: &#39;2020-05-29 09:46:58&#39;}, {&#39;wen&#39;: -26, &#39;time&#39;: &#39;2020-05-29 09:46:59&#39;}, {&#39;wen&#39;: 13, &#39;time&#39;: &#39;2020-05-29 09:47:00&#39;}, {&#39;wen&#39;: -20, &#39;time&#39;: &#39;2020-05-29 09:47:01&#39;}, {&#39;wen&#39;: 22, &#39;time&#39;: &#39;2020-05-29 09:47:02&#39;}, {&#39;wen&#39;: 3, &#39;time&#39;: &#39;2020-05-29 09:47:03&#39;}, {&#39;wen&#39;: 13, &#39;time&#39;: &#39;2020-05-29 09:47:04&#39;}, {&#39;wen&#39;: -24, &#39;time&#39;: &#39;2020-05-29 09:47:05&#39;}, {&#39;wen&#39;: -23, &#39;time&#39;: &#39;2020-05-29 09:47:06&#39;}] 后端代码示例
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E6%8A%98%E7%BA%BF%E5%9B%BE/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/sql%E8%AF%AD%E5%8F%A5%E6%9F%A5%E8%AF%A2%E7%89%B9%E5%AE%9A%E6%97%B6%E9%97%B4%E6%AE%B5%E5%86%85%E7%9A%84%E6%95%B0%E6%8D%AE/">sql语句查询特定时间段内的数据</a>
  </h1>
  <time datetime="2020-05-27T20:03:59&#43;0800" class="post-date">Wed, May 27, 2020</time>
  数据库
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `userId` bigint(20) NOT NULL, `fullName` varchar(64) NOT NULL, `userType` varchar(16) NOT NULL, `addedTime` datetime NOT NULL, PRIMARY KEY (`userId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_user -- ---------------------------- INSERT INTO `t_user` VALUES (&#39;1&#39;, &#39;爽爽&#39;, &#39;普通&#39;, &#39;2018-01-21 10:20:09&#39;); INSERT INTO `t_user` VALUES (&#39;2&#39;, &#39;贵贵&#39;, &#39;普通&#39;, &#39;2017-11-06 10:20:22&#39;); INSERT INTO `t_user` VALUES (&#39;3&#39;, &#39;芬芬&#39;, &#39;vip&#39;, &#39;2017-11-13 10:20:42&#39;); INSERT INTO `t_user` VALUES (&#39;4&#39;, &#39;思思&#39;, &#39;vip&#39;, &#39;2018-01-21 10:20:55&#39;); INSERT INTO `t_user` VALUES (&#39;5&#39;, &#39;妍妍&#39;, &#39;vip&#39;, &#39;2017-09-17 10:21:28&#39;); 1、下面是sql语句：
  
  <div class="read-more-link">
    <a href="/my_technology_blog/sql%E8%AF%AD%E5%8F%A5%E6%9F%A5%E8%AF%A2%E7%89%B9%E5%AE%9A%E6%97%B6%E9%97%B4%E6%AE%B5%E5%86%85%E7%9A%84%E6%95%B0%E6%8D%AE/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E7%9B%B8%E5%85%B3/">redis相关</a>
  </h1>
  <time datetime="2020-05-24T16:16:33&#43;0800" class="post-date">Sun, May 24, 2020</time>
  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E6%95%B0%E6%8D%AE%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/">redis数据淘汰策略</a>
  </h1>
  <time datetime="2020-05-24T14:09:44&#43;0800" class="post-date">Sun, May 24, 2020</time>
  noeviction：当内存不足以容纳新写入数据时，新写入操作会报错
　allkeys-lru：当内存不足以容纳新写入数据时，在键空间中，移除最近最少使用的key（这个是最常用的）
　allkeys-random：当内存不足以容纳新写入数据时，在键空间中，随机移除某个key，这个一般没人用
　volatile-lru：当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，移除最近最少使用的key（这个一般不太合适）
　volatile-random：当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，随机移除某个key
　volatile-ttl：当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，有更早过期时间的key优先移除
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E9%9B%AA%E5%B4%A9%E5%87%BB%E7%A9%BF%E7%A9%BF%E9%80%8F/">redis雪崩、击穿、穿透</a>
  </h1>
  <time datetime="2020-05-24T14:02:59&#43;0800" class="post-date">Sun, May 24, 2020</time>
  1. 穿透1）定义
 缓存穿透是指查询一个一定不存在的数据，由于缓存不命中，接着查询数据库也无法查询出结果， 虽然也不会写入到缓存中，但是这将会导致每个查询都会去请求数据库，造成缓存穿透； 2）解决方法 ：布隆过滤 对所有可能查询的参数以hash形式存储，在控制层先进行校验，不符合则丢弃，从而避免了对底层存储系统的查询压力；  2. 雪崩
1）定义　 缓存雪崩是指，由于缓存层承载着大量请求，有效的保护了存储层，但是如果缓存层由于某些原因整体不能提供服务 于是所有的请求都会达到存储层，存储层的调用量会暴增，造成存储层也会挂掉的情况。 2）解决方法 保证缓存层服务高可用性：比如 Redis Sentinel 和 Redis Cluster 都实现了高可用 依赖隔离组件为后端限流并降级：比如对某个key只允许一个线程查询数据和写缓存，其他线程等待。  3. 击穿
1）定义：
 缓存击穿，就是说某个 key 非常热点，访问非常频繁，处于集中式高并发访问的情况 当这个 key 在失效的瞬间，大量的请求就击穿了缓存，直接请求数据库，就像是在一道屏障上凿开了一个洞。 2）解决方法 解决方式也很简单，可以将热点数据设置为永远不过期； 或者基于 redis or zookeeper 实现互斥锁，等待第一个请求构建完缓存之后，再释放锁，进而其它请求才能通过该 key 访问数据  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/redis%E6%8C%81%E4%B9%85%E5%8C%96/">redis持久化</a>
  </h1>
  <time datetime="2020-05-24T13:55:58&#43;0800" class="post-date">Sun, May 24, 2020</time>
  redis持久化有两种反方式 1.RDB持久化 RDB持久化是把当前进程数据生成快照保存到硬盘的过程，触发RDB持久化过程分为手动触发和自动触发
1）触发机制 手动触发分别对应save和bgsave命令
·save命令：阻塞当前Redis服务器，直到RDB过程完成为止，对于内存 比较大的实例会造成长时间阻塞，线上环境不建议使用
·bgsave命令：Redis进程执行fork操作创建子进程，RDB持久化过程由子 进程负责，完成后自动结束。阻塞只发生在fork阶段，一般时间很短
2）自动触发RDB的持久 1）使用save相关配置，如“save m n”。表示m秒内数据集存在n次修改 时，自动触发bgsave。
2）如果从节点执行全量复制操作，主节点自动执行bgsave生成RDB文件并发送给从节点，更多细节见6.3节介绍的复制原理。
3）执行debug reload命令重新加载Redis时，也会自动触发save操作。
4）默认情况下执行shutdown命令时，如果没有开启AOF持久化功能则 自动执行bgsave。
bgsave是主流的触发RDB持久化方式
1）执行bgsave命令，Redis父进程判断当前是否存在正在执行的子进 程，如RDB/AOF子进程，如果存在bgsave命令直接返回。
2）父进程执行fork操作创建子进程，fork操作过程中父进程会阻塞，通 过info stats命令查看latest_fork_usec选项，可以获取最近一个fork操作的耗时，单位为微秒
3）父进程fork完成后，bgsave命令返回“Background saving started”信息并不再阻塞父进程，可以继续响应其他命令。
4）子进程创建RDB文件，根据父进程内存生成临时快照文件，完成后 对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的 时间，对应info统计的rdb_last_save_time选项。
5）进程发送信号给父进程表示完成，父进程更新统计信息，具体见 info Persistence下的rdb_*相关选项。
RDB文件的处理
保存：RDB文件保存在dir配置指定的目录下，文件名通过dbfilename配 置指定。可以通过执行config set dir{newDir}和config set dbfilename{newFileName}运行期动态执行，当下次运行时RDB文件会保存到新目录。
RDB的优缺点 RDB的优点： ·RDB是一个紧凑压缩的二进制文件，代表Redis在某个时间点上的数据 快照。非常适用于备份，全量复制等场景。比如每6小时执行bgsave备份， 并把RDB文件拷贝到远程机器或者文件系统中（如hdfs），用于灾难恢复。
·Redis加载RDB恢复数据远远快于AOF的方式。
RDB的缺点： ·RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运 行都要执行fork操作创建子进程，属于重量级操作，频繁执行成本过高。
·RDB文件使用特定二进制格式保存，Redis版本演进过程中有多个格式 的RDB版本，存在老版本Redis服务无法兼容新版RDB格式的问题。
针对RDB不适合实时持久化的问题，Redis提供了AOF持久化方式来解决。
2.AOF持久化 AOF（append only file）持久化：以独立日志的方式记录每次写命令， 重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用 是解决了数据持久化的实时性，目前已经是Redis持久化的主流方式
1）使用AOF 开启AOF功能需要设置配置：appendonly yes，默认不开启。AOF文件名 通过appendfilename配置设置，默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致，通过dir配置指定。AOF的工作流程操作：命令写入 （append）、文件同步（sync）、文件重写（rewrite）、重启加载 （load）
1）所有的写入命令会追加到aof_buf（缓冲区）中。 2）AOF缓冲区根据对应的策略向硬盘做同步操作。 AOF为什么把命令追加到aof_buf中？Redis使用单线程响应命令，如 果每次写AOF文件命令都直接追加到硬盘，那么性能完全取决于当前硬盘负 载。先写入缓冲区aof_buf中，还有另一个好处，Redis可以提供多种缓冲区同步硬盘的策略，在性能和安全性方面做出平衡
  
  <div class="read-more-link">
    <a href="/my_technology_blog/redis%E6%8C%81%E4%B9%85%E5%8C%96/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/rbac/">RBAC</a>
  </h1>
  <time datetime="2020-05-24T13:53:42&#43;0800" class="post-date">Sun, May 24, 2020</time>
  权限系统与RBAC模型概述  RBAC（Role-Based Access Control ）基于角色的访问控制。
在20世纪90年代期间，大量的专家学者和专门研究单位对RBAC的概念进行了深入研究，先后提出了许多类型的RBAC模型，其中以美国George Mason大学信息安全技术实验室（LIST）提出的RBAC96模型最具有系统性，得到普遍的公认。
RBAC认为权限的过程可以抽象概括为：判断【Who是否可以对What进行How的访问操作（Operator）】这个逻辑表达式的值是否为True的求解过程。
即将权限问题转换为Who、What、How的问题。who、what、how构成了访问权限三元组。
RBAC支持公认的安全原则：最小特权原则、责任分离原则和数据抽象原则。
 最小特权原则得到支持，是因为在RBAC模型中可以通过限制分配给角色权限的多少和大小来实现，分配给与某用户对应的角色的权限只要不超过该用户完成其任务的需要就可以了。 责任分离原则的实现，是因为在RBAC模型中可以通过在完成敏感任务过程中分配两个责任上互相约束的两个角色来实现，例如在清查账目时，只需要设置财务管理员和会计两个角色参加就可以了。 数据抽象是借助于抽象许可权这样的概念实现的，如在账目管理活动中，可以使用信用、借方等抽象许可权，而不是使用操作系统提供的读、写、执行等具体的许可权。但RBAC并不强迫实现这些原则，安全管理员可以允许配置RBAC模型使它不支持这些原则。因此，RBAC支持数据抽象的程度与RBAC模型的实现细节有关。  RBAC96是一个模型族，其中包括RBAC0~RBAC3四个概念性模型。
1、基本模型RBAC0定义了完全支持RBAC概念的任何系统的最低需求。
2、RBAC1和RBAC2两者都包含RBAC0，但各自都增加了独立的特点，它们被称为高级模型。
RBAC1中增加了角色分级的概念，一个角色可以从另一个角色继承许可权。
RBAC2中增加了一些限制，强调在RBAC的不同组件中在配置方面的一些限制。
3、RBAC3称为统一模型，它包含了RBAC1和RBAC2，利用传递性，也把RBAC0包括在内。这些模型构成了RBAC96模型族。
RBAC模型简述
RBAC0的模型中包括用户（U）、角色（R）和许可权（P）等3类实体集合。
RABC0权限管理的核心部分，其他的版本都是建立在0的基础上的，看一下类图：
RBAC0定义了能构成一个RBAC控制系统的最小的元素集合。
在RBAC之中,包含用户users(USERS)、角色roles(ROLES)、目标objects(OBS)、操作operations(OPS)、许可权permissions(PRMS)五个基本数据元素，此模型指明用户、角色、访问权限和会话之间的关系。
每个角色至少具备一个权限，每个用户至少扮演一个角色；可以对两个完全不同的角色分配完全相同的访问权限；会话由用户控制，一个用户可以创建会话并激活多个用户角色，从而获取相应的访问权限，用户可以在会话中更改激活角色，并且用户可以主动结束一个会话。
用户和角色是多对多的关系，表示一个用户在不同的场景下可以拥有不同的角色。
例如项目经理也可以是项目架构师等；当然了一个角色可以给多个用户，例如一个项目中有多个组长，多个组员等。
这里需要提出的是，将用户和许可进行分离，是彼此相互独立，使权限的授权认证更加灵活。
角色和许可（权限）是多对多的关系，表示角色可以拥有多分权利，同一个权利可以授给多个角色都是非常容易理解的，想想现实生活中，当官的级别不同的权限的情景，其实这个模型就是对权限这方面的一个抽象，联系生活理解就非常容易了。
RBAC1，基于RBAC0模型，引入角色间的继承关系，即角色上有了上下级的区别，角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系，允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构，实现角色间的单继承。
这种模型合适于角色之间的层次明确，包含明确。
RBAC2，基于RBAC0模型的基础上，进行了角色的访问控制。
RBAC2模型中添加了责任分离关系。RBAC2的约束规定了权限被赋予角色时，或角色被赋予用户时，以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束与用户-角色-权限关系一起决定了RBAC2模型中用户的访问许可，此约束有多种。
 互斥角色 ：同一用户只能分配到一组互斥角色集合中至多一个角色，支持责任分离的原则。互斥角色是指各自权限互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色，不能同时获得两个角色的使用权。常举的例子：在审计活动中，一个角色不能同时被指派给会计角色和审计员角色。 基数约束 ：一个角色被分配的用户数量受限；一个用户可拥有的角色数目受限；同样一个角色对应的访问权限数目也应受限，以控制高级权限在系统中的分配。例如公司的领导人有限的； 先决条件角色 ：可以分配角色给用户仅当该用户已经是另一角色的成员；对应的可以分配访问权限给角色，仅当该角色已经拥有另一种访问权限。指要想获得较高的权限，要首先拥有低一级的权限。就像我们生活中，国家主席是从副主席中选举的一样。 运行时互斥 ：例如，允许一个用户具有两个角色的成员资格，但在运行中不可同时激活这两个角色。  RBAC3，也就是最全面级的权限管理，它是基于RBAC0的基础上，将RBAC1和RBAC2进行整合了，最前面，也最复杂的：
综上为权限管理模型的相关介绍，其实在任何系统中都会涉及到权限管理的模块，无论复杂简单，我们都可以通过以RBAC模型为基础，进行相关灵活运用来解决我们的问题。
RBAC的优缺点
RBAC模型没有提供操作顺序控制机制。这一缺陷使得RBAC模型很难应用关于那些要求有严格操作次序的实体系统。
例如，在购物控制系统中要求系统对购买步骤的控制，在客户未付款之前不应让他把商品拿走。RBAC模型要求把这种控制机制放到模型
实用的RBAC模型的数据库建模  以下模型均来自于互联网
1、扩展RBAC用户角色权限设计方案
http://www.cnblogs.com/zwq194/archive/2011/03/07/1974821.html
RBAC（Role-Based Access Control，基于角色的访问控制），就是用户通过角色与权限进行关联。简单地说，一个用户拥有若干角色，每一个角色拥有若干权限。这样，就构造成“用户-角色-权限”的授权模型。在这种模型中，用户与角色之间，角色与权限之间，一般者是多对多的关系。（如下图）
角色是什么？可以理解为一定数量的权限的集合，权限的载体。例如：一个论坛系统，“超级管理员”、“版主”都是角色。版主可管理版内的帖子、可管理版内的用户等，这些是权限。要给某个用户授予这些权限，不需要直接将权限授予用户，可将“版主”这个角色赋予该用户。 当用户的数量非常大时，要给系统每个用户逐一授权（授角色），是件非常烦琐的事情。这时，就需要给用户分组，每个用户组内有多个用户。除了可给用户授权外，还可以给用户组授权。这样一来，用户拥有的所有权限，就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。（下图为用户组、用户与角色三者的关联关系）
在应用系统中，权限表现成什么？对功能模块的操作，对上传文件的删改，菜单的访问，甚至页面上某个按钮、某个图片的可见性控制，都可属于权限的范畴。有些权限设计，会把功能操作作为一类，而把文件、菜单、页面元素等作为另一类，这样构成“用户-角色-权限-资源”的授权模型。而在做数据表建模时，可把功能操作和资源统一管理，也就是都直接与权限表进行关联，这样可能更具便捷性和易扩展性。（见下图）
请留意权限表中有一列“权限类型”，我们根据它的取值来区分是哪一类权限，如“MENU”表示菜单的访问权限、“OPERATION”表示功能模块的操作权限、“FILE”表示文件的修改权限、“ELEMENT”表示页面元素的可见性控制等。 这样设计的好处有二。其一，不需要区分哪些是权限操作，哪些是资源，（实际上，有时候也不好区分，如菜单，把它理解为资源呢还是功能模块权限呢？）。其二，方便扩展，当系统要对新的东西进行权限控制时，我只需要建立一个新的关联表“权限XX关联表”，并确定这类权限的权限类型字符串。 这里要注意的是，权限表与权限菜单关联表、权限菜单关联表与菜单表都是一对一的关系。（文件、页面权限点、功能操作等同理）。也就是每添加一个菜单，就得同时往这三个表中各插入一条记录。这样，可以不需要权限菜单关联表，让权限表与菜单表直接关联，此时，须在权限表中新增一列用来保存菜单的ID，权限表通过“权限类型”和这个ID来区分是种类型下的哪条记录。 到这里，RBAC权限模型的扩展模型的完整设计图如下：
随着系统的日益庞大，为了方便管理，可引入角色组对角色进行分类管理，跟用户组不同，角色组不参与授权。例如：某电网系统的权限管理模块中，角色就是挂在区局下，而区局在这里可当作角色组，它不参于权限分配。另外，为方便上面各主表自身的管理与查找，可采用树型结构，如菜单树、功能树等，当然这些可不需要参于权限分配。
2、百度百科所示的模型
3、本文参考文献中的一种设计
  
  <div class="read-more-link">
    <a href="/my_technology_blog/rbac/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%8E%A8%E8%8D%90%E7%AE%97%E6%B3%95/">推荐算法</a>
  </h1>
  <time datetime="2020-05-14T14:28:16&#43;0800" class="post-date">Thu, May 14, 2020</time>
  推荐算法是计算机专业中的一种算法，通过一些数学算法，推测出用户可能喜欢的东西，目前应用推荐算法比较好的地方主要是网络，其中淘宝做的比较好。  发展背景：
 推荐算法的研究起源于20世纪90年代，由美国明尼苏达大学 GroupLens研究小组最先开始研究，他们想要制作一个名为 Movielens的电影推荐系统，从而实现对用户进行电影的个性化推荐。首先研究小组让用户对自己看过的电影进行评分，然后小组对用户评价的结果进行分析，并预测出用户对并未看过的电影的兴趣度，从而向他们推荐从未看过并可能感兴趣的电影。此后， Amazon开始在网站上使用推荐系统，在实际中对用户的浏览购买行为进行分析，尝试对曾经浏览或购买商品的用户进行个性化推荐。根据 enture Beat的统计，这一举措将该网站的销售额提高了35%自此之后，个性化推荐的应用越来越广泛。  推荐算法分类：
基于内容：项目或对象是通过相关特征的属性来定义的，系统基于用户评价对象的特征、学习用户的兴趣，考察用户资料与待预测项目的匹配程度。用户的资料模型取决于所用的学习方法，常用的有决策树、神经网络和基于向量的表示方法等。基于内容的用户资料需要有用户的历史数据，用户资料模型可能随着用户的偏好改变而发生变化。
基于协同：基于协同过滤的推荐算法( Collaborative Filtering Recommendation)技术是推荐系统中应用最早和最为成功的技术之一。它一般采用最近邻技术，利用用户的历史喜好信息计算用户之间的距离，然后利用目标用户的最近邻居用户对商品评价的加权评价值来预测目标用户对特定商品的喜好程度，从而根据这一喜好程度来对目标用户进行推荐。
基于关联规则：以关联规则为基础，把已购商品作为规则头，规则体为推荐对象。关联规则就是在一个交易数据库中统计购买了商品集X的交易中有多大比例的交易同时购买了商品集y，其直观的意义就是用户在购买某些商品的时候有多大倾向去购买另外一些商品。比如购买牛奶的同时很多人会购买面包。
基于效用：基于效用的推荐（Utility-based Recommendation）是建立在对用户使用项目的效用情况上计算的，其核心问题是怎样为每一个用户去创建一个效用函数，因此，用户资料模型很大程度上是由系统所采用的效用函数决定的。
基于知识：基于知识的方法因它们所用的功能知识不同而有明显区别。效用知识( FunctionalKnowledge)是一种关于一个项目如何满足某一特定用户的知识，因此能解释需要和推荐的关系，所以用户资料可以是任何能支持推理的知识结构，它可以是用户已经规范化的查询，也可以是一个更详细的用户需要的表示。
组合推荐：由于各种推荐方法都有优缺点，所以在实际中，组合推荐( Hybrid Recommendation)经常被采用。研究和应用最多的是内容推荐和协同过滤推荐的组合。
推荐方法 优点 缺点 基于内容推荐	推荐结果直观，容易解释；不需要领域知识 新用户问题；复杂属性不好处理；要有足够数据构造分类器 协同过滤推荐	新异兴趣发现、不需要领域知识；随着时间推移性能提高；推荐个性化、自动化程度高；能处理复杂的非结构化对象	稀疏问题；可扩展性问题；新用户问题；质量取决于历史数据集；系统开始时推荐质量差； 基于规则推荐	能发现新兴趣点；不要领域知识 规则抽取难、耗时；产品名同义性问题；个性化程度低； 基于效用推荐	无冷开始和稀疏问题；对用户偏好变化敏感；能考虑非产品特性 用户必须输入效用函数；推荐是静态的，灵活性差；属性重叠问题； 基于知识推荐	能把用户需求映射到产品上；能考虑非产品属性 知识难获得；推荐是静态的
协同过滤推荐：
1.启发式推荐算法（Memory-based algorithms）
启发式推荐算法易于实现，并且推荐结果的可解释性强。启发式推荐算法又可以分为两类：
基于用户的协同过滤（User-based collaborative filtering）：主要考虑的是用户和用户之间的相似度，只要找出相似用户喜欢的物品，并预测目标用户对对应物品的评分，就可以找到评分最高的若干个物品推荐给用户。举个例子，李老师和闫老师拥有相似的电影喜好，当新电影上映后，李老师对其表示喜欢，那么就能将这部电影推荐给闫老师。
基于物品的协同过滤（Item-based collaborative filtering）：主要考虑的是物品和物品之间的相似度，只有找到了目标用户对某些物品的评分，那么就可以对相似度高的类似物品进行预测，将评分最高的若干个相似物品推荐给用户。举个例子，如果用户A、B、C给书籍X,Y的评分都是5分，当用户D想要买Y书籍的时候，系统会为他推荐X书籍，因为基于用户A、B、C的评分，系统会认为喜欢Y书籍的人在很大程度上会喜欢X书籍。
2.基于模型的推荐算法（Model-based algorithms）
基于模型的推荐算法利用矩阵分解，有效的缓解了数据稀疏性的问题。矩阵分解是一种降低维度的方法，对特征进行提取，提高推荐准确度。基于模型的方法包括决策树、基于规则的模型、贝叶斯方法和潜在因素模型。
推荐框架:tensorflow,pytorch
推荐系统的目的：
1.帮助用户快速找到想要的商品，提高用户对网站的忠诚度；
2.提高网站交叉销售能力、成交转化率；
流程
首先，找到User1 喜欢的商品；
找出与User1具有相同的商品兴趣爱好的人群；
找出该人群喜欢的其他商品；
将这些商品推送给User1。
例子
两名用户都在某电商网站购买了A、B两种产品。当他们产生购买这个动作的时候，两名用户之间的相似度便被计算了出来。其中一名用户除了购买了产品A和B，还购买了C产品，此时推荐系统会根据两名用户之间的相似度会为另一名用户推荐项目C。
难点1：如何获取兴趣相似的用户
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E6%8E%A8%E8%8D%90%E7%AE%97%E6%B3%95/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E7%A7%92%E6%9D%80%E6%80%9D%E8%B7%AF/">秒杀思路</a>
  </h1>
  <time datetime="2020-05-14T14:26:11&#43;0800" class="post-date">Thu, May 14, 2020</time>
  秒杀和一般的商品订单有什么不一样？
秒杀的请求量比较大，我们用秒杀来模拟企业中有些业务的高并发寻求。
如何提高秒杀中的请求处理数量？（提高并发量）
前端：
 1.限制流量，过滤掉一些请求（有些请求直接前端处理，符合发送规则的在进行发送） 2.页面静态化：将这个页面所要展示的数据，提前的更新到页面中写死。那么用户在请求页面的时候就不需要到后端去拿取数据了。  后端：
 1.mysql优化。 2.代码的优化。 3.使用缓存系统来存储热点数据。 4.对热点数据，已经存在的用redis查询，redis没查到，去mysql里查询，查询结果存放在redis，方便下一次读数据 5.服务器的硬件升级 6.mysql主从，redis主从，nginx启动。  
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/">全文检索</a>
  </h1>
  <time datetime="2020-05-12T10:30:32&#43;0800" class="post-date">Tue, May 12, 2020</time>
  全文检索django-haystack+jieba+whoosh
全文检索里的组件简介
　1、什么是haystack？
　1. haystack是django的开源搜索框架，该框架支持Solr,Elasticsearch,Whoosh, Xapian搜索引擎，不用更改代码，直接切换引擎，减少代码量。
　2. 搜索引擎使用Whoosh，这是一个由纯Python实现的全文搜索引擎，没有二进制文件等，比较小巧，配置比较简单，当然性能自然略低。
　3. 中文分词Jieba，由于Whoosh自带的是英文分词，对中文的分词支持不是太好，故用jieba替换whoosh的分词组件。
　2、什么是jieba？
　1、很多的搜索引擎对中的支持不友好，jieba作为一个中文分词器就是加强对中文的检索功能
　3、Whoosh是什么？
　1、Python的全文搜索库，Whoosh是索引文本及搜索文本的类和函数库
　2、Whoosh 自带的是英文分词，对中文分词支持不太好，使用 jieba 替换 whoosh 的分词组件。
haystack配置使用（前后端分离）
安装工具
pip install django-haystack pip install whoosh pip install jieba 　在setting.py中配置
&#39;&#39;&#39;注册app &#39;&#39;&#39; INSTALLED_APPS = [ &#39;django.contrib.admin&#39;, &#39;django.contrib.auth&#39;, &#39;django.contrib.contenttypes&#39;, &#39;django.contrib.sessions&#39;, &#39;django.contrib.messages&#39;, &#39;django.contrib.staticfiles&#39;, # haystack要放在应用的上面 &#39;haystack&#39;, &#39;jsapp&#39;, # 这个jsapp是自己创建的app ] ​
&#39;&#39;&#39; 模板路径 &#39;&#39;&#39; TEMPLATES = [ { &#39;DIRS&#39;: [os.path.join(BASE_DIR,&#39;templates&#39;)], }, ] ​
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E5%9F%BA%E4%BA%8E%E9%98%BF%E9%87%8C%E4%BA%91%E6%89%8B%E6%9C%BA%E9%AA%8C%E8%AF%81%E7%A0%81/">基于阿里云手机验证码</a>
  </h1>
  <time datetime="2020-05-12T10:14:30&#43;0800" class="post-date">Tue, May 12, 2020</time>
  下载
pip install aliyun-python-sdk-core 导包
# 阿里云短信 from aliyunsdkcore.client import AcsClient from aliyunsdkcore.request import CommonRequest redis连接
#定义ip地址和端口 host = &#34;120.26.175.79&#34; port = 6379 #链接对象 r = redis.Redis(host=host, port=port, decode_responses=True) 生成验证码函数
def get_random_str(): #验证码是由 字母a~z A~Z 数字 0~9 组成 # 在 ascii 码中 小写a的起点是97 大写A起点是65 a_ = [chr(var) for var in range(97, 123)] # 小写a的列表推导式 A_ = [chr(var) for var in range(65, 91)] # 大写A的列表推导式 num_ = [str(var) for var in range(0, 9)] # 数字的 # 使用sample 在列表中随机生成6个任意字母数字 return &#39;&#39;.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E5%9F%BA%E4%BA%8E%E9%98%BF%E9%87%8C%E4%BA%91%E6%89%8B%E6%9C%BA%E9%AA%8C%E8%AF%81%E7%A0%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E9%82%AE%E7%AE%B1%E9%AA%8C%E8%AF%81%E7%A0%81/">邮箱验证码</a>
  </h1>
  <time datetime="2020-05-12T10:11:51&#43;0800" class="post-date">Tue, May 12, 2020</time>
  在setting中进行配置
EMAIL_USE_SSL = True # Secure Sockets Layer 安全套接层, 取决于邮件服务器是否开启加密协议 EMAIL_HOST = &#39;smtp.qq.com&#39; # 邮件服务器地址 ， 如果是163改成smtp.163.com EMAIL_PORT = 465 # 邮件服务器端口 EMAIL_HOST_USER = &#39;1349157185@qq.com&#39; # 登陆邮件服务器的账号 EMAIL_HOST_PASSWORD = &#39;zdcyocfdngqfhcde&#39; # 登陆邮件服务器的授权密码,要在邮箱里进行设置 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # 邮件的发送者 # 允许哪些人可以请求你和请求方式 CORS_ORIGIN_ALLOW_ALL = True # 改为true是所有人都可以访问我 view导包
from django.http import JsonResponse from django.core.mail import send_mail # 用来发送用 from vue001project.settings import EMAIL_HOST_USER # 导入setting中的配置 from vue001project.settings import DEFAULT_FROM_EMAIL	# 导入setting中的配置 import redis # 导入redis，存储验证码，后期用于验证 redis连接及配置
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E9%82%AE%E7%AE%B1%E9%AA%8C%E8%AF%81%E7%A0%81/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/go-lang%E6%89%93%E9%80%A0%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2/">利用基于Go Lang的Hugo配合nginx来打造属于自己的纯静态博客系统</a>
  </h1>
  <time datetime="2020-04-19T15:33:05&#43;0800" class="post-date">Sun, Apr 19, 2020</time>
  Go lang无疑是目前的当红炸子鸡，极大地提高了后端编程的效率，同时有着极高的性能。借助Go语言我们 可以用同步的方式写出高并发的服务端软件，同时，Go语言也是云原生第一语言，Docker，Kubernetes等等著名的项目都是使用Go语言实现的。
 Hugo 是基于 Go 语言的静态网站生成器。
 什么是静态网站生成器？顾名思义，就是以最快的速度生成一个高可用的web页面，我们知道Django作为一款非常流行的框架被广泛应用，但是部署起来实在是太麻烦了，各种命令各种配置，动态页面必然要涉及数据库的配置和操作，另外只要涉及数据库操作，你就不得不考虑sql注入等一系列的安全因素。而纯静态页面则没有这个烦恼，如果我们只需要一个简单的站点，一些简单的功能和页面，比如博客，我们只想以极简的方式用markdown语法写下文字或者代码，让服务器访问纯静态页面，这就是静态网站生成器可以提供给我们的好处。
 为啥选择Hugo?
 Hugo是一种go语言编写的静态网站生成器。适用于搭建个人blog、公司主页、help等网站，是一种小型的CMS系统。静态站点的好处就是快速、安全、易于部署，方便管理。
Hugo的优点包含但不限于:
得益于Go的高性能，性能很快
世界上最快的静态网站生成工具，5秒生成6000个页面
文档为Markdown格式,语法超简单
Hugo 可以做静态文件生成工具，还是高性能web 服务；
丰富的站点迁移工具，可以将wordpress，Ghost，Jekyll，DokuWiki，Blogger轻松迁移至 Hugo
超详细的文档
活跃的社区
更加自由的内容组织方式
丰富的主题模板，可以让你的网站更加炫目多彩
 多环境支持：macos ，linux，windows
 首先，可以去go的官网网站下载安装包 https://golang.org/dl/ 然后直接双击安装即可，不需要配置环境变量，因为安装过程自动配置，安装完毕后，打开命令行，输入
go version  显示主版本号即表示安装成功
 然后，可以进行hugo的在线源码编译安装，打开命令行，输入下面的命令
go get -u -v github.com/spf13/hugo go build -o hugo main.go mv hugo $GOPATH/bin  如果你不想在线编译安装，也可以去hugo的官网 https://github.com/gohugoio/hugo/releases 下载稳定版的压缩包，解压之后配置一下环境变量也可以
 装完以后，在命令行内输入
hugo version  打印出版本号即表示hugo安装成功
 在命令行中输入命令
hugo new site hugo_blog  就生成了一个名字为hugo_blog的新站点，可以感受到速度非常快，和vue.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/go-lang%E6%89%93%E9%80%A0%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E4%B8%AA%E4%BA%BA%E7%AE%80%E5%8E%86/">个人简历</a>
  </h1>
  <time datetime="2020-04-08T15:47:45&#43;0800" class="post-date">Wed, Apr 8, 2020</time>
  基本信息
 杨博 1994年6月
北京 Python全栈开发工程师
4年工作经验
手机：178-3916-0323
邮箱：ybo2756@gmail.com
个人网站: http://yang_bo_ha_ha.gitee.io/my_technology_blog/
个人简介
 互联网行业从业四年(具有四年的开发经验)，掌握前端 vue 框架，作为主程参与设计与开发过多个项目，负责系统核心模块的开发，测试与自动化部署。深刻理解算法和数据结构，并可以将其融合到业务中去。熟练使用django，善于设计高可用项目架构。关注前端业务规范，掌握vue.js开发,了解微信小程序开发，了解 HTTP 协议、掌握websocket，掌握 MySQL,redis,Mongodb 数据库。有优秀的学习能力和团队沟通能力，经常与团队进行技术分享，能与团队共同成长。
教育经历
 2016.08 - 2019.06 安阳职业技术学院
技能列表
  后端框架：Django,Flask 前端框架：Vue.js,Mpvue 数据库：MySQL,Redis,Mongodb 工具：Docker,Git 其他：微信小程序/公众号开发, HTTP, Websocket 外语：CET6，能流畅阅读英文文档  工作经历
  2018年7月 - 至今 钱方好近(北京)科技有限公司—高级项目经理 2016年9月 - 2018年5月 北京谐云科技有限公司—初级python开发工程师  近期参与项目（顺序为倒序，最新的项目排第一位，同时第一位也是你的主力项目，你必须熟悉能说，并且知识点要全，同时项目之间的时间点要连起来，不要断开）
 主导钱方好近移动聚合支付平台开发(2019年1月-2019年8月)
项目简介：解决商户与各个支付平台复杂的支付逻辑，统一支付码，不通过现金结算即可按照商户需要展开个性化定制，利用与各第三方支付平台以及合作银行等的聚合，所开展的一项支付工具多样化的综合支付业务
 基于Tornado实现Restful风格的在线聚合支付接口，聚合封装了支付宝，微信，京东等三方支付平台 使用Mysql存储数据，配置主从热备，水平分表。 采用Celery和Rabbitmq异步任务队列架构，同时配置自动化定时任务 利用Websocket实现后端消息主动推送，改造前端传统轮询技术框架，减少了30%的网络请求数，节约了大约一半的可用带宽 使用Redis集群作为缓存介质，缓解数据库压力。 利用Docker进行服务封装和业务解耦，使用Docker-compose批量管理容器集群，用Docker-file编写部署脚本 Nginx反向代理Tornado，采用加权策略的负载均衡技术，后台服务统一使用SuperVisor进行管理 利用gensim对用户投诉及评论信息进行模糊匹配与情感分析，预测用户导向。 后期使用Thrift框架RPC协议架构对传统的http接口进行重构，提高了整体接口的性能和吞吐量。 使用Redisearch打造全文检索引擎，百万级数据可以达到单次检索10毫秒以内的速度。 利用FastDFS结合FastDHT实现用户上传文件的分布式去重存储。 开发，测试用户认证，订单，支付/退款等7个模块  参与宝宝树线上产品功能的预研和开发测试
项目简介：宝宝树是一款以书籍形式记载宝宝成长，交流育儿心得的产品。包含五大模块，成长书，交流，服务，商城和我。涵盖功能为书籍行事记录宝宝点滴，发帖沟通交流，线上求助咨询和线下亲子活动，网上商城购买宝宝用品。
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E4%B8%AA%E4%BA%BA%E7%AE%80%E5%8E%86/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E6%8E%92%E8%A1%8C%E6%A6%9C/">排行榜逻辑</a>
  </h1>
  <time datetime="2020-04-08T15:43:26&#43;0800" class="post-date">Wed, Apr 8, 2020</time>
  设想在一个游戏中，有上百万的玩家数据，如果现在需要你根据玩家的经验值整理一个前20名的排行榜，你会怎么做呢？ 一般的做法是写一条类似下面这条sql语句的方式来获取
select * from game_socre order by score desc limit 0,20 实现这个功能主要用到的redis数据类型是redis的有序集合zset。zset 是set 类型的一个扩展,比原有的类型多了一个顺序属性.此属性在每次插入数据时会自动调整顺序值,保证value值按照一定顺序连续排列。
主要的实现思路是： 1、在一个新的玩家参与到游戏中时，在redis中的zset中新增一条记录（记录内容看具体的需求）score为0 2、当玩家的经验值发生变化时，修改该玩家的score值 3、使用redis的ZREVRANGE方法获取排行榜
#课程点击数统计 def course_click(request): cid = request.GET.get(&#39;cid&#39;) #使用redis有序集合来记录 r.zincrby(&#39;course-click&#39;,1,int(cid)) res = {} res[&#39;code&#39;] = 200 res[&#39;message&#39;] = &#39;页面访问成功&#39; return HttpResponse(json.dumps(res,ensure_ascii=False),content_type=&#39;application/json&#39;) #获取排行榜前n位的数据 def get_top_n_users(num): #获取所有有序集合元素 进行切片操作 course_clicks = r.zrevrange(&#39;course-click&#39;,0,-1,withscores=True)[:num] #和mysql联动 course = Course.objects.in_bulk([int(item[0]) for item in course_clicks]) #将点击数和mysql结果集结合 res = [] #遍历 for item in course_clicks: try: res.append({int(item[1]):course[int(item[0])]}) except Exception as e: print(str(e)) pass return res #获取排行榜真实数据 def get_course_list(request): #调用数据 get_course = get_top_n_users(3) res = [] #拼装json for dic in get_course: for k,v in dic.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E6%8E%92%E8%A1%8C%E6%A6%9C/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/jwt/">JSON Web Token</a>
  </h1>
  <time datetime="2020-04-08T15:41:12&#43;0800" class="post-date">Wed, Apr 8, 2020</time>
  pip install pyjwt JWT 特点 体积小，因而传输速度快
传输方式多样，可以通过URL/POST参数/HTTP头部等方式传输
严格的结构化。它自身（在 payload 中）就包含了所有与用户相关的验证消息，如用户可访问路由、访问有效期等信息，服务器无需再去连接数据库验证信息的有效性，并且 payload 支持为你的应用而定制化。
支持跨域验证，可以应用于单点登录。
JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案，编码之后的JWT看起来是这样的一串字符：
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 由 . 分为三段，通过解码可以得到：
  头部（Header）
// 包括类别（typ）、加密算法（alg）； { &ldquo;alg&rdquo;: &ldquo;HS256&rdquo;, &ldquo;typ&rdquo;: &ldquo;JWT&rdquo; }
  jwt的头部包含两部分信息：
声明类型，这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
载荷（payload）  载荷（payload） 载荷就是存放有效信息的地方。
签名（signature)  签名的目的：签名实际上是对头部以及载荷内容进行签名。所以，如果有人对头部以及载荷的内容解码之后进行修改，再进行编码的话，那么新的头部和载荷的签名和之前的签名就将是不一样的。而且，如果不知道服务器加密的时候用的密钥的话，得出来的签名也一定会是不一样的。 这样就能保证token不会被篡改。
最后，我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候，我们还需要提供一个密钥（secret）。类似盐
这里在第三步我们得到 JWT 之后，需要将JWT存放在 client，之后的每次需要认证的请求都要把JWT发送过来。（请求时可以放到 header 的 Authorization ）
一、使用JSON Web Token的好处？ 1.性能问题。 JWT方式将用户状态分散到了客户端中，相比于session，可以明显减轻服务端的内存压力。 Session方式存储用户id的最大弊病在于Session是存储在服务器端的，所以需要占用大量服务器内存， 对于较大型应用而言可能还要保存许多的状态，一般还需借助nosql和缓存机制来实现session的存储，如果是分布式应用还需session共享。 2.单点登录。 JWT能轻松的实现单点登录，因为用户的状态已经被传送到了客户端。 token 可保存自定义信息，如用户基本信息，web服务器用key去解析token，就获取到请求用户的信息了。 我们也可以配置它以便包含用户拥有的任何权限。这意味着每个服务不需要与授权服务交互才能授权用户。 3.前后端分离。 以前的传统模式下，后台对应的客户端就是浏览器，就可以使用session+cookies的方式实现登录， 但是在前后分离的情况下，后端只负责通过暴露的RestApi提供数据，而页面的渲染、路由都由前端完成。因为rest是无状态的，因此也就不会有session记录到服务器端。 4.兼容性。 支持移动设备，支持跨程序调用，Cookie 是不允许垮域访问的，而 Token 则不存在这个问题。 5.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/jwt/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%87%E6%BB%A4%E5%99%A8/">自定义过滤器</a>
  </h1>
  <time datetime="2020-04-08T15:13:00&#43;0800" class="post-date">Wed, Apr 8, 2020</time>
  随意一个子应用中创建一个包文件夹templatetags（templatetags文件夹中包含init.py文件）。
  在templatetags中创建自定义到py文件。
 from django.template import Library register = Library() @register.filter('percent_decimal') def percent_decimal(value): value = str(value) + &quot;元&quot; return value    模板中使用自定义过滤器
  自定义标签
什么是模板标签（标签）
模板标签都是放在{％}}括号里的，常见的模板标签有{％loadxxxx％}，{％blockxxxx％}，{％ifxxx％}，{％url&rsquo;xxxx&rsquo;％}。这些模板标签的本质也是函数，标签名一般即为函数名。这些标签的主要作用包括载入代码渲染模板或对传递过来的参数进行一定的逻辑判断或计算后返回。
自定义标签相对于自定义过滤器来说要复杂很多，因为自定义标签可以做任何事情！
 自定义标签分为很多类型    简单标签 Simple tags
from django.template import Library from datetime import datetime
register = Library()
@register.simple_tag def current_time(format_str): return datetime.now().strftime(format_str)
    Library.simple_tag(takes_context=True) takes_context=True参数可以让我们访问模板的当前环境上下文，即将当前环境上下文中的参数和值作为字典传入函数中的一个名为context的参数
from django.template import Library from datetime import datetime
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%87%E6%BB%A4%E5%99%A8/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/vue%E7%9A%84%E5%AE%89%E8%A3%85/">vue的安装</a>
  </h1>
  <time datetime="2020-04-03T15:20:01&#43;0800" class="post-date">Fri, Apr 3, 2020</time>
  安装node.js 安装npm:npm install -g cnpm &ndash;registry=https://registry.npm.taobao.org 使用cnpm安装vue:cnpm install vue -g 安装vue命令行工具：cnpm install vue-cli -g 安装vue-devtools调试工具： 创建vue脚手架:vue init webpack myproject  vue init webpack myproject cd myproject // 进入目录 cnpm install // 安装依赖 cnpm run dev // 开启服务  src 主开发目录，所有的单文件组件都会放在这个目录下 static 项目静态目录，所有的css、js都会放在这个文件夹下 dist 项目打包发布文件夹，最后要上线单文件夹项目都在这个文件夹中 - node_modules node的包目录 config 配置目录 build 项目打包时依赖的目录     常用命令  v-html:将内容按照html格式进行插入 v-text:将内容按照文本格式进行插入，但会覆盖原有标签内的内容，不会有加载的闪烁问题 v-cloak:解决加载时的闪烁问题   绑定属性:v-bind 或者 直接写 冒号 如果我们需要设置的模板变量是一个属性，比如a标签的href属性 绑定事件：v-on 或者 @ 符号:给元素绑定对应事件。 表单绑定 v-model： 动态绑定  当某些情况下，无法确定表单中所代表的属性值，可以使用v-bind进行动态绑定，v-&gt; model获取到的表单输入 此时则是我们定义的v-bind属性值
  
  <div class="read-more-link">
    <a href="/my_technology_blog/vue%E7%9A%84%E5%AE%89%E8%A3%85/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E8%B7%A8%E5%9F%9F%E5%A4%84%E7%90%86/">vue跨域</a>
  </h1>
  <time datetime="2020-04-03T15:20:01&#43;0800" class="post-date">Fri, Apr 3, 2020</time>
  vue跨域配制（第一种方法）
在config文件夹下的index.js中配制
proxyTable: {&#39;/api&#39;: { //使用&#34;/api&#34;来代替&#34;http://f.apiplus.c&#34;  target: &#39;http://127.0.0.1:8000/&#39;, //源地址  changeOrigin: true, //改变源  pathRewrite: { &#39;^/api&#39;: &#39;&#39; //路径重写  } } }跨域（第二种方法）
用Django的第三方包 django-cors-headers 来解决跨域问题
 操作步骤：    pip install django-cors-headers  在settings.py中添加corsheaders.middleware.CorsMiddleware,在SessionMiddleware和CommonMiddleware的中间  在settings.py中添加CORS_ORIGIN_ALLOW_ALL = Trueaxios
 安装 axios  cnpm install --save axios 配制axios  在src文件下的mian.js中配制
import axios from &#39;axios&#39;Vue.prototype.axios = axiosaxios使用axios完整写法：this.axios({method: &#39;post&#39;,url: &#39;/user/12345&#39;,data: {firstName: &#39;Fred&#39;,lastName: &#39;Flintstone&#39;}}).
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E8%B7%A8%E5%9F%9F%E5%A4%84%E7%90%86/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/router/">router</a>
  </h1>
  <time datetime="2019-05-23T00:00:00Z" class="post-date">Thu, May 23, 2019</time>
  { // a为局部变量 let a = 1; // b为全局变量 var b = 2; } // b不会报错 console.log(b); // a会报错，因为作用域 console.log(a); 跳转
router-link
跳转到hello  tag:指定渲染成什么标签.
 默认是 显示按钮效果：hello button    replace:不会留下历史记录，浏览器的后退按钮不可用。
  &lt;router-link to=&#34;/hello&#34; tag=&#34;li&#34; replace&gt;hello li&lt;/router-link&gt;     按钮跳转 跳转到hellomethods:{ handleRouter:function () { this.$router.push(&#39;/hello&#39;) } }  $router还有一些方法：   replace，跳转后，不会留下历史记录。
  go,类似于window.history.go(),在history向前或向后多少步，参数是整数。 后退前进back:function () { // 后退2步  this.$router.go(-2); }, next:function () { // 前进1步  this.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/router/">Read More…</a>
  </div>
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E7%AE%80%E5%8D%95%E7%90%86%E8%A7%A3/"></a>
  </h1>
  <time datetime="0001-01-01T00:00:00Z" class="post-date">Mon, Jan 1, 0001</time>
  三次握手  第一次握手：客户端给服务器发送一个 syn 包，这是客户端进入等待连接状态 第二次握手：服务器接收到 syn 包，然后发送一个 ack+syn 包，表示我收到了 第三次握手：客户端接收到 ack+syn 包，这时再发送一个 ack 包，这个包发送完毕后，服务器和客户端就连接上了，三次握手也到此结束  通俗理解 就是：
 Client：嘿，李四，是我，听到了吗？ Server：我听到了，你能听到我的吗? Client：好的，我们互相都能听到对方的话，我们的通信可以开始了。  ​
四次挥手  第一次挥手：主动关闭方向被动关闭方发送一个 FIN 包，告诉被动关闭方 我的数据已经发送完了。这时主动关闭方还可以接收数据 第二次握手：被动关闭方接收到 FIN 包，给主动关闭方发送一个 ACK 包。 第三次挥手：被动关闭方发送一个 FIN 包，告诉主动关闭方我的数据也发送完了，可以关闭连接 第四次挥手：主动关闭方接收到 FIN 包，再发送一个 ACK 包，发送完毕。四次挥手到此结束  通俗理解 就是：
 Client：我所有东西都说完了 Server：我已经全部听到了，但是等等我，我还没说完 Server：好了，我已经说完了 Client：好的，那我们的通信结束l  常见问题  为什么握手要三次，挥手要四次  因为当Server端收到Client端的SYN连接请求报文后，可以直接发送SYN+ACK报文。其中ACK报文是用来应答的，SYN报文是用来同步的。但是关闭连接时，当Server端收到FIN报文时，很可能并不会立即关闭SOCKET，所以只能先回复一个ACK报文，告诉Client端，&ldquo;你发的FIN报文我收到了&rdquo;。只有等到我Server端所有的报文都发送完了，我才能发送FIN报文，因此不能一起发送。故需要四步握手。    
  
</article><article class="post">
  <h1 class="post-title">
    <a href="/my_technology_blog/%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81/">图片验证码</a>
  </h1>
  <time datetime="0001-01-01T00:00:00Z" class="post-date">Mon, Jan 1, 0001</time>
  图形验证码用于登录、注册等方面，安全性相对较低
1、图形验证码首先应该验证码类（django端）
# 定义验证码类 class MyCode(View): # 定义随机颜色 def get_random_color(self): R = random.randrange(255) G = random.randrange(255) B = random.randrange(255) return (R, G, B) # 获取验证码视图 def get(self, request): # 画布 img_size = (200, 100) # 定义画图对象 image = Image.new(&#39;RGB&#39;, img_size, &#39;white&#39;) # 定义画笔对象 draw = ImageDraw.Draw(image, &#39;RGB&#39;) # 定义随机字符串 source = &#39;hvhsdfjhkbaskjdfbvhkj&#39; # 容器 code_str = &#39;&#39; # 定义字体(本地路径) my_font = ImageFont.truetype(font=&#34;C:\\Windows\\Fonts\\framdit.ttf&#34;, size=50) for i in range(4): # 获取随机颜色 text_color = self.
  
  <div class="read-more-link">
    <a href="/my_technology_blog/%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81/">Read More…</a>
  </div>
  
</article>
</div>
    </main>

    
  </body>
</html>
